Componentes · 19 min de lectura

Matriz LED MAX7219 con Arduino: texto en movimiento y reloj

Matriz LED MAX7219 con Arduino: conexión de módulos 8x8 y 4 en 1 8x32, texto en movimiento con MD_Parola, texto en espejo con FC16_HW, reloj y ESP32.

El MAX7219 es un chip controlador barato que maneja 64 LED él solo, y es la forma más sencilla de usar una matriz LED con Arduino. Lo encontrarás sobre todo en dos versiones: un módulo con una sola matriz de puntos 8x8 y un módulo 4 en 1 de 8x32 puntos con cuatro matrices una al lado de otra. El MAX7219 solo necesita 3 cables de señal del Arduino (DIN, CS, CLK); él mismo se encarga de barrer las filas cientos de veces por segundo, de ajustar el brillo y de recordar el estado de cada LED. Así el Arduino queda libre para texto en movimiento, relojes, marcadores o animaciones, y puedes alargar la pantalla tanto como quieras encadenando módulos.

En este artículo aprenderás cómo funciona el MAX7219, cómo conectar el módulo por SPI a un Arduino Uno, un ESP32 y una Raspberry Pi Pico, cómo encadenar módulos y cómo mostrar tu primer texto en movimiento con la librería MD_Parola. Después resolverás el problema del texto en espejo o al revés, el que más atasca a los principiantes, eligiendo el tipo de hardware correcto (FC16_HW); calcularás el brillo y el consumo, mostrarás tildes, la ñ y caracteres propios, y construirás un reloj con un módulo RTC y una animación píxel a píxel. Las últimas secciones explican cómo enviar mensajes desde el móvil con un ESP32, el código MicroPython para la Pico y las soluciones a los errores más comunes.

Módulo LED de matriz de puntos MAX7219 4 en 1 de 8x32
Módulo de matriz de puntos MAX7219 4 en 1: cuatro matrices 8x8 con un MAX7219 debajo de cada una; se alimenta por el lado IN y se conecta al siguiente módulo por el lado OUT.

¿Qué es el MAX7219 y cómo funciona?

No es posible conectar 64 LED uno a uno a los pines del Arduino, por eso las matrices LED usan multiplexación. En una matriz 8x8 los LED están conectados en una rejilla de 8 filas y 8 columnas. En cada momento, el MAX7219 activa solo un grupo de LED (un "digit" en la hoja de datos, que en la matriz es una fila o una columna), indica por las demás líneas cuáles de los ocho LED de ese grupo se encienden y pasa al siguiente grupo. Los ocho grupos se recorren unas 800 veces por segundo; el ojo no puede seguir esa velocidad, así que la imagen parece fija. Mientras tanto, el chip guarda el patrón de cada grupo en su propia RAM de 8x8 bits, limita la corriente de los LED con una sola resistencia y regula el brillo en 16 niveles.

El Arduino se comunica con el MAX7219 mediante una interfaz serie de 3 hilos que funciona de forma parecida a SPI. Cada orden es un paquete de 16 bits: el byte alto indica en qué registro se escribe y el byte bajo lleva el valor. Mientras CS (LOAD en la hoja de datos) está en LOW, los bits entran por DIN en cada flanco de subida de CLK; cuando CS vuelve a HIGH, el paquete se guarda en el registro. Es la misma idea que el registro de desplazamiento 74HC595, pero como el MAX7219 también se ocupa del barrido y de limitar la corriente, da mucho menos trabajo. Encontrarás cómo funciona SPI en general en ¿Qué es SPI?.

CaracterísticaValor
Tensión de alimentación4,0–5,5 V (en la práctica, 5 V)
InterfazSerie de 3 hilos (DIN, CLK, CS), hasta 10 MHz
LED que controla64 (una matriz 8x8 u 8 dígitos de 7 segmentos)
Frecuencia de barrido800 Hz típicos (8 dígitos)
Corriente por segmentoSe fija con la resistencia RSET, máximo 40 mA (en los módulos suele ser de 10 kΩ)
Brillo16 niveles, ciclo de trabajo de 1/32 a 31/32
Corriente en modo apagado (shutdown)Unos 150 µA
Umbral HIGH de entradaAl menos 3,5 V (con alimentación de 5 V)
EncadenadoLa salida DOUT va a la entrada DIN del siguiente chip

El MAX7219 tiene 14 registros direccionables, pero los que necesitas para una matriz LED están en la tabla de abajo. Las librerías los configuran por ti; aun así, saber para qué sirven ayuda mucho al buscar fallos. Por ejemplo, al recibir alimentación por primera vez el chip arranca en modo shutdown con la pantalla apagada, de modo que si el código de inicialización no se ejecuta, el módulo parece no responder en absoluto. Su hermano, el MAX7221, tiene los mismos registros, pero añade un pin CS realmente compatible con SPI y drivers de segmento con pendiente limitada que reducen las interferencias electromagnéticas.

DirecciónRegistroFunción
0x00No-OpDeja este chip sin cambios mientras un paquete va a otro chip de la cadena
0x01–0x08Digit 0–7El patrón de 8 bits de cada una de las ocho filas (o columnas)
0x09Decode ModeDecodificación BCD para 7 segmentos; desactivada (0x00) en una matriz
0x0AIntensityBrillo, de 0x0 a 0xF
0x0BScan LimitNúmero de dígitos que se barren; 7 (los ocho) en una matriz
0x0CShutdown0: modo apagado, 1: funcionamiento normal
0x0FDisplay Test1: todos los LED se encienden al máximo brillo

¿Módulo 8x8 individual o módulo 4 en 1 de 8x32?

Los dos módulos que verás con más frecuencia tienen el mismo circuito; la diferencia está en cómo se conecta la matriz LED al MAX7219 y en la forma de la placa. El módulo individual de placa verde lleva un MAX7219 en encapsulado DIP (24 patillas) y una matriz 8x8 como la 1088AS, con los pines de entrada (VCC, GND, DIN, CS, CLK) en un extremo y los de salida (VCC, GND, DOUT, CS, CLK) en el otro. En el módulo 4 en 1 de placa azul, cuatro MAX7219 SMD van debajo de las matrices y los cuatro bloques vienen encadenados de fábrica; la placa suele llevar la marca FC-16. Para un texto largo en movimiento, el módulo 4 en 1 es más barato y más ordenado.

Módulo de matriz LED MAX7219 8x8 individual
Módulo MAX7219 8x8 individual: placa verde, MAX7219 en encapsulado DIP y matriz de puntos 8x8; en el código suele mostrarse bien con el tipo de hardware GENERIC_HW.
CaracterísticaMódulo 8x8 individualMódulo 4 en 1 de 8x32 (FC-16)
Resolución8x8 = 64 LED32x8 = 256 LED
Número de MAX72191 (encapsulado DIP)4 (encapsulado SMD)
Tipo de hardware en el códigoNormalmente GENERIC_HWNormalmente FC16_HW
Caracteres visibles a la vez14–6 (según el ancho de las letras)
Corriente máxima con brillo al máximoUnos 330 mAUnos 1,3 A
Uso idealIconos, dado, caritas, juegos pequeñosTexto en movimiento, relojes, marcadores

Conexión del MAX7219: Arduino Uno, ESP32 y Pico

El módulo tiene cinco pines: VCC y GND para la alimentación, DIN para los datos, CLK para el reloj y CS para la selección del chip (carga). Por defecto, MD_Parola y MD_MAX72XX usan SPI por hardware; en ese caso DIN debe ir al pin MOSI de tu placa y CLK al pin SCK, mientras que para CS puedes elegir cualquier pin digital. Puedes comprobar dónde están los pines SPI por hardware de tu placa con la herramienta Pinout. Conecta siempre los cables al lado de entrada (IN) del módulo, el extremo marcado como DIN; el extremo marcado como DOUT es para el siguiente módulo.

Pin del móduloArduino UnoESP32Raspberry Pi Pico
VCC5V5V (VIN)VBUS (5 V)
GNDGNDGNDGND
DIND11 (MOSI)GPIO 23 (MOSI)GP19 (SPI0 TX)
CSD10GPIO 5GP17
CLKD13 (SCK)GPIO 18 (SCK)GP18 (SPI0 SCK)
No alimentes un módulo MAX7219 desde el pin de 3,3 V; el chip necesita al menos 4 V. Incluso a 5 V, un solo módulo 8x8 puede consumir más de 300 mA con el brillo al máximo, y un módulo 4 en 1 hasta 1,3 A. El puerto USB del ordenador y el fusible USB del Arduino están limitados a 500 mA. Si el Arduino se reinicia o la imagen parpadea al subir el brillo, alimenta el módulo con una fuente externa de 5 V (al menos 2 A para un módulo 4 en 1) y conecta siempre el GND de la fuente al GND del Arduino.

Los pines del ESP32 y de la Pico son de 3,3 V, mientras que la hoja de datos del MAX7219 pide al menos 3,5 V como nivel HIGH con alimentación de 5 V. Es decir, las señales de 3,3 V quedan técnicamente un poco por debajo del límite. La mayoría de los módulos funcionan igualmente, pero con cables largos o con algunos lotes de chips pueden aparecer puntos encendidos al azar e imágenes corruptas. La solución fiable es un buffer como el 74HCT125, que acepta 3,3 V como HIGH y entrega 5 V, o un conversor de nivel lógico rápido en las líneas DIN, CLK y CS. Mantener los cables de señal por debajo de 20 cm también suele resolver el problema.

Encadenar módulos (daisy chain)

El pin DOUT del MAX7219 saca los datos que entraron por DIN 16,5 ciclos de reloj después. Cuando unes el lado OUT de un módulo con el lado IN del siguiente, DOUT va al DIN del chip siguiente, mientras que CLK y CS son comunes a todos. Así, del Arduino siguen saliendo solo 3 cables de señal, pero la pantalla puede tener 8, 16 o 32 bloques. En cada actualización la librería envía paquetes por toda la cadena y, cuando CS sube, todos se actualizan a la vez. En los módulos 4 en 1 esta conexión ya viene hecha en la placa; para formar una pantalla de 8 bloques (64x8) con dos módulos 4 en 1, basta con unir los pines OUT del primero a los pines IN del segundo y poner MAX_DEVICES a 8 en el código.

En la librería, la numeración de los bloques empieza en el módulo más cercano a la entrada DIN y aumenta hacia la izquierda; para MD_MAX72XX, el origen de coordenadas es la esquina superior derecha de la pantalla. Por eso la entrada DIN debe quedar a la derecha para que el texto se lea bien. Cuanto más larga es la cadena, más se convierte la alimentación en el verdadero límite: cada 4–8 bloques vuelve a llevar 5 V y GND a la cadena con cables gruesos y añade en la entrada de alimentación de cada módulo un condensador electrolítico de 10 µF y uno cerámico de 100 nF. La hoja de datos también recomienda esta pareja para suavizar el rizado que provocan los picos de corriente de los LED.

Instalación de las librerías: MD_MAX72XX y MD_Parola

Las dos librerías más usadas para el MAX7219 son del mismo autor (majicDesigns) y trabajan juntas. MD_MAX72XX hace el trabajo de bajo nivel: inicializa los módulos, escribe píxeles y columnas y gestiona la fuente. MD_Parola se apoya en ella y añade texto en movimiento, efectos, alineación y zonas. La librería LedControl que verás en ejemplos antiguos también funciona, pero tendrás que programar el desplazamiento del texto tú mismo. Si nunca has instalado una librería, lee primero Cómo añadir una librería en Arduino.

  1. En el Arduino IDE abre el Gestor de librerías (Herramientas > Administrar bibliotecas) y busca MD_Parola.
  2. Instala la librería MD_Parola de majicDesigns. Cuando te pregunte por las dependencias, elige Install all para que también se instale MD_MAX72XX.
  3. Conecta el módulo según la tabla anterior y, para un módulo 4 en 1, pon HARDWARE_TYPE a FC16_HW y MAX_DEVICES a 4 en el código.
  4. Sube el sketch. Si el texto aparece al revés, en espejo o girado, no te preocupes: lo corregirás con una sola línea en la sección del tipo de hardware.

Primer programa: texto en movimiento con MD_Parola

Arduino (C++)
1// MAX7219 4'lü modülde kayan yazı (MD_Parola) / Scrolling text on a 4-in-1 MAX7219 module (MD_Parola)
2#include <MD_Parola.h>
3#include <MD_MAX72xx.h>
4#include <SPI.h>
5
6#define HARDWARE_TYPE MD_MAX72XX::FC16_HW   // mavi PCB'li 4'lü modül / blue-PCB 4-in-1 module
7#define MAX_DEVICES 4                       // zincirdeki 8x8 blok sayısı / number of 8x8 blocks in the chain
8#define CS_PIN 10                           // DIN = D11 (MOSI), CLK = D13 (SCK)
9
10MD_Parola display = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
11
12void setup() {
13  display.begin();
14  display.setIntensity(4);                  // parlaklık 0–15 / brightness 0–15
15  display.displayClear();
16  // metin, hizalama, hız (ms/kare), bekleme (ms), giriş efekti, çıkış efekti
17  // text, alignment, speed (ms/frame), pause (ms), entry effect, exit effect
18  display.displayText("Merhaba BlueGrays!", PA_CENTER, 50, 1000, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
19}
20
21void loop() {
22  if (display.displayAnimate()) {           // efekt bittiğinde true döner / returns true when the effect ends
23    display.displayReset();                 // aynı yazıyı baştan başlat / restart the same text
24  }
25}

La línea más importante es displayText(): recibe, por este orden, el texto, la alineación (PA_LEFT, PA_CENTER, PA_RIGHT), el tiempo entre fotogramas en milisegundos, la pausa cuando el texto llega a su sitio y los efectos de entrada y salida. Cuanto menor es el valor de velocidad, más rápido se mueve el texto; entre 25 y 60 ms se consigue un desplazamiento legible. displayAnimate() avanza como mucho un fotograma cada vez que la llamas y vuelve enseguida. Por eso hay que llamarla continuamente en loop() y no debes usar delay() en el resto del código; si no, el texto avanzará a tirones. En el artículo millis() frente a delay() verás cómo esperar sin bloquear el programa.

FunciónQué hace
begin()Inicializa los módulos, los saca del modo apagado y borra la pantalla
displayText(texto, alineación, velocidad, pausa, entrada, salida)Define el texto y los efectos que se mostrarán e inicia la animación
displayAnimate()Dibuja el siguiente fotograma; devuelve true cuando el efecto termina
displayReset()Vuelve a reproducir el mismo texto y efecto desde el principio
print("...")Muestra texto fijo sin efecto
setIntensity(0–15)Ajusta el brillo de todos los módulos
setInvert(true)Intercambia los LED encendidos y apagados (imagen en negativo)
displayShutdown(true)Pone la pantalla en modo apagado; con false la vuelve a encender
begin(2) y setZone(0, 0, 1)Divide la pantalla en zonas independientes, cada una con su texto y su efecto

Además de PA_SCROLL_LEFT hay muchos efectos de entrada y salida: PA_PRINT (el texto aparece de golpe), PA_SCROLL_UP y PA_SCROLL_DOWN (desplazamiento vertical), PA_WIPE (barrido), PA_OPENING y PA_CLOSING (apertura y cierre desde el centro), PA_FADE (fundido con el brillo), PA_DISSOLVE, PA_BLINDS, PA_RANDOM y PA_GROW_UP. Para verlos todos, sube el ejemplo Parola_Animation_Catalog desde Archivo > Ejemplos > MD_Parola; no olvides cambiar las líneas HARDWARE_TYPE y MAX_DEVICES según tu módulo.

Elegir el tipo de hardware (FC16_HW) y corregir el texto en espejo

Cada fabricante conecta la matriz 8x8 al MAX7219 de una manera: en unas placas las líneas "digit" del chip van a las filas, en otras a las columnas, y el orden de filas o columnas puede estar invertido. La librería compensa esa diferencia con el ajuste HARDWARE_TYPE. Si eliges el tipo equivocado, el módulo no está averiado, pero las letras se ven en espejo, al revés o giradas 90 grados en cada bloque 8x8. Como muchos códigos antiguos de internet usan PAROLA_HW o GENERIC_HW, este es el problema más frecuente con los módulos 4 en 1 azules que se venden hoy. La tabla muestra qué cambiar según el síntoma; está pensada para el módulo 4 en 1 FC-16.

Lo que vesCausa probableSolución
Las letras se ven en espejo (izquierda-derecha) en cada bloquePAROLA_HW seleccionado, orden de columnas invertidoUsa FC16_HW
Las letras están giradas 90 grados en cada bloqueGENERIC_HW seleccionado, filas y columnas intercambiadasUsa FC16_HW
Las letras están boca abajo en cada bloqueICSTATION_HW seleccionado, orden de filas y columnas invertidoUsa FC16_HW
Todo el texto está boca abajo y se desplaza de izquierda a derechaEl módulo está girado, con la entrada DIN a la izquierdaGira el módulo 180 grados para que la entrada DIN quede a la derecha
Solo algunos bloques se ven bien, el resto está vacío o revueltoMAX_DEVICES no coincide con el número real de bloquesPon en MAX_DEVICES el número real de bloques
En el módulo 8x8 verde prueba primero GENERIC_HW y en el módulo 4 en 1 azul prueba primero FC16_HW. Si la imagen sigue mal, prueba los cuatro tipos uno tras otro; cada vez solo cambia una línea. Para averiguar con certeza el tipo de tu módulo, sube el sketch MD_MAX72xx_HW_Mapper de los ejemplos de MD_MAX72XX: enciende los LED uno a uno y muestra en el Monitor Serie qué fila y qué columna están conectadas a cada sitio.

Ajuste de brillo y consumo

El brillo depende de dos cosas. La primera es la resistencia RSET del módulo: el chip entrega a los LED unas 100 veces la corriente que sale del pin ISET. Los módulos suelen llevar 10 kΩ, lo que supone una corriente de pico algo superior a 40 mA con LED rojos y de unos 40 mA con LED azules y blancos. La segunda es el registro Intensity que ajustas por software: un valor n de 0 a 15 mantiene los LED encendidos durante (2n+1)/32 de cada intervalo de barrido, así que 0 es el brillo mínimo (1/32) y 15 el máximo (31/32). Además, como cada fila solo se enciende una octava parte del tiempo, incluso con el brillo al máximo un LED consume de media unos 5 mA. Puedes consultar las tensiones directas según el color del LED en la herramienta Tensiones de LED.

Puedes estimar de forma aproximada la corriente media de un módulo así: I ≈ 8 mA + 5 mA × (2n+1)/32 × L. Aquí, 8 mA es el consumo propio del chip, n el valor de brillo y L el número de LED encendidos en ese módulo (0–64); 5 mA es la octava parte de la corriente de pico de 40 mA. Por ejemplo, con brillo 15 y los 64 LED encendidos salen 8 + 5 × 0,97 × 64 ≈ 318 mA, lo que concuerda con los 330 mA de la hoja de datos. Con texto en movimiento normalmente solo se enciende entre el 25 y el 35 por ciento de los LED, así que el consumo real es mucho menor. La tabla muestra valores típicos para un módulo 4 en 1.

SituaciónPor móduloMódulo 4 en 1 (32x8)
Todos los LED encendidos, brillo 15≈ 320 mA≈ 1,3 A
Todos los LED encendidos, brillo 7≈ 160 mA≈ 630 mA
Texto en movimiento (~30 % de los LED), brillo 7≈ 55 mA≈ 210 mA
Texto en movimiento (~30 % de los LED), brillo 2≈ 25 mA≈ 90 mA
Modo apagado (displayShutdown)≈ 0,15 mA≈ 0,6 mA

Para atenuar la pantalla por la noche puedes ajustar el brillo automáticamente con un sensor de luz LDR y dormir la pantalla con displayShutdown(true) cuando no haya nadie. Si diseñas un cartel a pilas, introduce los valores anteriores en la herramienta Duración de la batería para calcular cuántas horas aguantará. En interiores suele bastar con un brillo de 2–4 en lugar de 15; cansa menos la vista y reduce la corriente de los LED a entre un tercio y una sexta parte, aproximadamente.

Tildes, ñ y caracteres especiales

El Arduino IDE guarda tu código en UTF-8, así que una letra como "ñ" o "á" no ocupa un byte en el texto, sino dos. MD_Parola busca cada byte como un número de carácter en la fuente, por eso las letras acentuadas aparecen como símbolos sin sentido. La fuente por defecto de la librería incluye todo el juego Latin-1 (á, é, í, ó, ú, ñ, ü, ¿, ¡ y más), de modo que basta una pequeña función que convierta UTF-8 a los códigos de la fuente. Las letras que no están en Latin-1, como las turcas ğ, ı y ş del ejemplo, hay que dibujarlas píxel a píxel y añadirlas con addChar(). El sketch hace las dos cosas.

Arduino (C++)
1// MAX7219 + MD_Parola: Türkçe karakterli kayan yazı / Scrolling text with Turkish letters
2#include <MD_Parola.h>
3#include <MD_MAX72xx.h>
4#include <SPI.h>
5
6#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
7#define MAX_DEVICES 4
8#define CS_PIN 10
9
10MD_Parola display = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
11
12// Fontta olmayan 6 harf: ilk bayt genişlik, sonrakiler sütunlar (bit 0 = en üst satır)
13// 6 letters missing from the font: first byte is the width, then the columns (bit 0 = top row)
14const uint8_t trG[] = {5, 56, 69, 70, 85, 116};  // Ğ
15const uint8_t trg[] = {4, 25, 166, 166, 125};    // ğ
16const uint8_t trI[] = {3, 68, 125, 68};          // İ
17const uint8_t tri[] = {1, 124};                  // ı
18const uint8_t trS[] = {5, 70, 73, 201, 73, 49};  // Ş
19const uint8_t trs[] = {4, 88, 212, 84, 52};      // ş
20
21// UTF-8 metni fontun kodlarına çevirir / converts UTF-8 text to the font's codes
22void utf8ToFont(const char *src, char *dst, size_t size) {
23  size_t n = 0;
24  while (*src && n < size - 1) {
25    uint8_t c = *src++;
26    if (c < 0x80) { dst[n++] = c; continue; }            // ASCII aynen kalır / ASCII stays as is
27    if ((c & 0xE0) == 0xC0 && *src) {                    // 2 baytlık harf / 2-byte letter
28      uint16_t u = ((c & 0x1F) << 6) | (*src++ & 0x3F);
29      switch (u) {
30        case 0x11E: dst[n++] = (char)0x80; break;        // Ğ
31        case 0x11F: dst[n++] = (char)0x81; break;        // ğ
32        case 0x130: dst[n++] = (char)0x82; break;        // İ
33        case 0x131: dst[n++] = (char)0x83; break;        // ı
34        case 0x15E: dst[n++] = (char)0x84; break;        // Ş
35        case 0x15F: dst[n++] = (char)0x85; break;        // ş
36        default: dst[n++] = (u < 0x100) ? (char)u : '?'; // ç ö ü Ç Ö Ü ° fontta var / already in the font
37      }
38      continue;
39    }
40    while ((*src & 0xC0) == 0x80) src++;                 // desteklenmeyen karakteri atla / skip an unsupported character
41    dst[n++] = '?';
42  }
43  dst[n] = '\0';
44}
45
46char message[96];
47
48void setup() {
49  display.begin();
50  display.setIntensity(3);
51  display.addChar(0x80, trG);                // addChar begin()'den sonra / addChar after begin()
52  display.addChar(0x81, trg);
53  display.addChar(0x82, trI);
54  display.addChar(0x83, tri);
55  display.addChar(0x84, trS);
56  display.addChar(0x85, trs);
57  utf8ToFont("Günaydın İstanbul! Şehirde hava 22°C, çok güzel bir gün.", message, sizeof(message));
58  display.displayText(message, PA_LEFT, 40, 1000, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
59}
60
61void loop() {
62  if (display.displayAnimate()) display.displayReset();
63}

En cada array de carácter propio, el primer byte indica el ancho de la letra en columnas y los siguientes son las columnas de izquierda a derecha; el bit 0 de cada byte es la fila superior. Por ejemplo, la ı turca (i sin punto) es una sola columna, y el valor 124 (0b01111100) enciende las filas 2 a 6; solo se diferencia de la i normal en que le falta el punto. En las mayúsculas Ğ e İ la letra se acortó dos filas (5 en lugar de 7) para dejar sitio al signo de arriba. addChar() no copia los datos, solo guarda su dirección; por eso los arrays deben ser globales y añadirse después de begin(). Se eligieron los códigos 0x80–0x85 porque nunca aparecen en un texto normal. Del mismo modo puedes añadir cualquier símbolo, como un corazón o una flecha.

Ejemplo de reloj: reloj digital con un módulo RTC

Módulo de reloj RTC DS1307
Módulo RTC DS1307: gracias a su pila de botón sigue contando la hora aunque el Arduino esté apagado; se conecta al bus I2C mediante los pines SDA y SCL.

El contador millis() del Arduino vuelve a cero al cortarse la corriente y, por el resonador cerámico del Uno, puede desviarse hasta varios minutos al día. Para un reloj de verdad usa un módulo de reloj RTC. Conecta el módulo DS1307 al bus I2C (SDA → A4, SCL → A5, VCC → 5V, GND → GND); el MAX7219 usa los pines SPI, así que los dos módulos no se estorban. Para el código, instala la librería RTClib de Adafruit desde el Gestor de librerías.

Arduino (C++)
1// MAX7219 + DS1307 RTC ile dijital saat / Digital clock with MAX7219 + DS1307 RTC
2#include <MD_Parola.h>
3#include <MD_MAX72xx.h>
4#include <SPI.h>
5#include <RTClib.h>                         // Adafruit RTClib
6
7#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
8#define MAX_DEVICES 4
9#define CS_PIN 10
10
11MD_Parola display = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
12RTC_DS1307 rtc;                             // SDA = A4, SCL = A5
13
14char timeText[8];                           // "12:34" + sonlandırıcı / + terminator
15uint8_t lastSecond = 99;
16
17void setup() {
18  display.begin();
19  display.setIntensity(2);
20  display.setTextAlignment(PA_CENTER);      // print() bu hizalamayı kullanır / print() uses this alignment
21  if (!rtc.begin()) {
22    display.print("RTC?");                  // modül bulunamadı / module not found
23    while (true) {}
24  }
25  if (!rtc.isrunning()) {
26    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));   // ilk kurulumda derleme saati / compile time on first run
27  }
28}
29
30void loop() {
31  DateTime now = rtc.now();
32  if (now.second() != lastSecond) {         // saniyede bir güncelle / update once a second
33    lastSecond = now.second();
34    // ':' ve boşluk aynı genişlikte, yazı kaymaz / ':' and space are equally wide, nothing shifts
35    sprintf(timeText, (lastSecond % 2) ? "%02d %02d" : "%02d:%02d", now.hour(), now.minute());
36    display.print(timeText);
37  }
38}

El sketch lee la hora una vez por segundo y hace parpadear los dos puntos. En la fuente por defecto, los dos puntos (:) y el espacio tienen el mismo ancho (2 columnas), así que los dígitos no se mueven. La primera vez que funciona, el RTC se pone en la hora en que se compiló el sketch. Un DS1307 puede desviarse alrededor de un minuto al mes; para un reloj más preciso, compra un módulo DS3231 y usa en el código RTC_DS3231 en lugar de RTC_DS1307 y cambia la condición if (!rtc.isrunning()) por if (rtc.lostPower()). Gracias a su cristal con compensación de temperatura, el DS3231 solo se desvía cerca de un minuto al año. También puedes dividir la pantalla en dos zonas para mostrar la hora en un lado y la temperatura en el otro.

Animación: dibujar píxel a píxel con MD_MAX72XX

Para ir más allá del texto en movimiento puedes usar directamente la librería MD_MAX72XX. Esta trata toda la cadena como una única pantalla de píxeles: un módulo 4 en 1 tiene 32 columnas y 8 filas. setPoint(fila, columna, estado) enciende o apaga un solo LED, setColumn() escribe de una vez los 8 bits de una columna y clear() borra la pantalla. El origen (0, 0) es la esquina superior derecha; los números de columna aumentan hacia la izquierda y los de fila hacia abajo. El siguiente código dibuja una pelota que rebota en los bordes de la pantalla:

Arduino (C++)
1// MD_MAX72XX ile seken top animasyonu / Bouncing ball animation with MD_MAX72XX
2#include <MD_MAX72xx.h>
3#include <SPI.h>
4
5#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
6#define MAX_DEVICES 4
7#define CS_PIN 10
8
9MD_MAX72XX mx = MD_MAX72XX(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
10
11int x = 0, y = 0;                           // topun konumu (sütun, satır) / ball position (column, row)
12int dx = 1, dy = 1;                         // hareket yönü / direction of travel
13
14void setup() {
15  mx.begin();
16  mx.control(MD_MAX72XX::INTENSITY, 2);
17  mx.clear();
18}
19
20void loop() {
21  const int lastCol = mx.getColumnCount() - 1;          // 4 modülde 31 / 31 with 4 modules
22  mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::OFF);      // çizim bitene kadar gönderme / hold updates while drawing
23  mx.setPoint(y, x, false);                             // eski konumu sil / erase the old position
24  x += dx;
25  y += dy;
26  if (x <= 0 || x >= lastCol) dx = -dx;                 // kenara çarpınca dön / bounce off the edge
27  if (y <= 0 || y >= 7) dy = -dy;
28  mx.setPoint(y, x, true);                              // yeni konumu yak / light the new position
29  mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::ON);       // hepsini tek seferde gönder / send everything at once
30  delay(40);
31}

En cada fotograma primero se desactiva la actualización automática (UPDATE), se borra el píxel anterior y se dibuja el nuevo; después se vuelve a activar UPDATE para que los cambios lleguen a los módulos de una sola vez. Así nunca se ve un fotograma a medio dibujar. Con la misma idea puedes dibujar un dado, caritas, el juego de la serpiente o barras de nivel de sonido. Los ejemplos de MD_MAX72XX incluyen además animaciones listas como Pac-Man, ojos de robot y un reloj de arena. Si usas MD_Parola, accedes a las mismas funciones de dibujo con getGraphicObject().

Mensajes por internet con un ESP32: texto en movimiento desde el móvil

Con un ESP32 puedes convertir tu letrero de texto en movimiento en un letrero inalámbrico. El siguiente código se conecta a tu red doméstica, primero desplaza su propia dirección IP por la pantalla y después muestra la hora obtenida de internet (NTP). Abre esa dirección IP en el navegador de un móvil conectado a la misma Wi-Fi y verás un cuadro de texto; el mensaje que escribas empezará a desplazarse en la siguiente vuelta y se alternará con la hora. El módulo se conecta a los pines VSPI como en la tabla: CLK al GPIO 18, DIN al GPIO 23 y CS al GPIO 5. Si es tu primera vez con el ESP32, lee antes ¿Qué es el ESP32?.

Arduino (C++)
1// ESP32: telefondan mesaj + internet saati / ESP32: message from your phone + internet time
2#include <WiFi.h>
3#include <WebServer.h>
4#include <time.h>
5#include <MD_Parola.h>
6#include <MD_MAX72xx.h>
7#include <SPI.h>
8
9#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
10#define MAX_DEVICES 4
11#define CS_PIN 5                            // VSPI: CLK = GPIO 18, DIN = GPIO 23
12
13const char* ssid = "AgAdi";                 // Wi-Fi adın / your Wi-Fi name
14const char* password = "Sifre123";          // Wi-Fi şifren / your Wi-Fi password
15
16MD_Parola display = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
17WebServer server(80);
18
19char message[128];
20char nextMessage[128];
21char clockText[8] = "--:--";
22bool newMessage = false;
23bool showClock = false;
24
25const char PAGE[] = R"rawliteral(<!DOCTYPE html><html><head><meta charset="utf-8">
26<meta name="viewport" content="width=device-width"><title>MAX7219</title></head><body>
27<h2>Kayan yazı / Scrolling text</h2>
28<form action="/"><input name="m" maxlength="100"> <button>Gönder / Send</button></form>
29</body></html>)rawliteral";
30
31void handleRoot() {
32  if (server.hasArg("m")) {                 // formdan gelen mesaj / message from the form
33    server.arg("m").toCharArray(nextMessage, sizeof(nextMessage));
34    newMessage = true;
35  }
36  server.send(200, "text/html; charset=utf-8", PAGE);
37}
38
39void setup() {
40  display.begin();
41  display.setIntensity(3);
42  display.print("WiFi..");
43  WiFi.begin(ssid, password);
44  while (WiFi.status() != WL_CONNECTED) delay(500);
45  configTime(3 * 3600, 0, "pool.ntp.org");  // Türkiye UTC+3 / Turkey is UTC+3
46  snprintf(message, sizeof(message), "IP: %s", WiFi.localIP().toString().c_str());
47  server.on("/", handleRoot);
48  server.begin();
49  display.displayText(message, PA_LEFT, 40, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
50}
51
52void loop() {
53  server.handleClient();
54  if (display.displayAnimate()) {           // önceki gösterim bitti / the previous show has ended
55    showClock = !showClock;
56    if (newMessage) {                       // yeni mesaj sırayı keser / a new message jumps the queue
57      strlcpy(message, nextMessage, sizeof(message));
58      newMessage = false;
59      showClock = false;
60    }
61    if (showClock) {
62      struct tm t;
63      if (getLocalTime(&t, 10)) strftime(clockText, sizeof(clockText), "%H:%M", &t);
64      display.displayText(clockText, PA_CENTER, 40, 3000, PA_SCROLL_DOWN, PA_SCROLL_UP);
65    } else {
66      display.displayText(message, PA_LEFT, 40, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
67    }
68  }
69}

handleRoot() no manda el mensaje del formulario directamente a la pantalla, sino que lo deja esperando en nextMessage. El mensaje se copia solo cuando la animación en curso termina (cuando displayAnimate() devuelve true); así el texto que MD_Parola está leyendo nunca cambia a mitad de camino. El valor 3 × 3600 de configTime() corresponde a la zona horaria UTC+3 de Turquía; en la España peninsular sustituye esa línea por configTzTime("CET-1CEST,M3.5.0,M10.5.0/3", "pool.ntp.org"), que además tiene en cuenta el horario de verano, y en otros países pon tu propia diferencia horaria. Para mensajes con tildes y ñ, añade a este sketch las líneas addChar() y la función utf8ToFont() de arriba, y cambia la línea de strlcpy() por utf8ToFont(nextMessage, message, sizeof(message)). Para la hora por internet y paneles web en tiempo real, consulta Reloj NTP con ESP32 y Servidor web y WebSocket con ESP32.

MAX7219 con Raspberry Pi Pico y MicroPython

En la Pico con MicroPython, lo más práctico es copiar el driver de código abierto max7219.py (micropython-max7219) a la Pico con Thonny. Como el driver usa el módulo framebuf de MicroPython, trae listas funciones de dibujo como text(), pixel(), line() y rect(), y funciona con la orientación correcta en los módulos 4 en 1 FC-16. Alimenta el módulo desde VBUS (5 V) y conecta DIN a GP19, CLK a GP18 y CS a GP17. MicroPython también reserva el pin MISO de SPI0 (GP16), así que no lo uses para otra cosa. La fuente de framebuf es de 8x8 píxeles y solo ASCII, por lo que un módulo 4 en 1 muestra 4 letras a la vez y no muestra tildes. Para la puesta en marcha, consulta ¿Qué es la Raspberry Pi Pico? y Fundamentos de MicroPython.

MicroPython
1# Pico + 4'lü MAX7219 modül: kayan yazı / Pico + 4-in-1 MAX7219 module: scrolling text
2from machine import Pin, SPI
3import time
4import max7219                        # max7219.py Pico'da olmalı / must be on the Pico
5
6# GP18 = SCK -> CLK, GP19 = MOSI -> DIN, GP17 -> CS; GP16 (MISO) boş kalır / GP16 (MISO) stays free
7spi = SPI(0, baudrate=5_000_000, polarity=0, phase=0, sck=Pin(18), mosi=Pin(19))
8display = max7219.Matrix8x8(spi, Pin(17, Pin.OUT), 4)
9display.brightness(3)                 # parlaklık 0–15 / brightness 0–15
10
11def scroll(text, delay_ms=35):
12    width = len(text) * 8             # her harf 8 piksel / each letter is 8 px wide
13    for x in range(32, -width, -1):   # sağdan girip solda kaybolur / enters right, leaves left
14        display.fill(0)
15        display.text(text, x, 0, 1)
16        display.show()
17        time.sleep_ms(delay_ms)
18
19while True:
20    scroll("BlueGrays + Pico")
21    display.fill(0)
22    display.text("Pico", 0, 0, 1)     # 4 harf tam sığar / exactly 4 letters fit
23    display.show()
24    time.sleep(1)
25    display.fill(0)
26    display.rect(0, 0, 32, 8, 1)      # ekran çerçevesi / display frame
27    display.show()
28    time.sleep_ms(500)

Ideas de proyectos con el MAX7219

  • Letrero de texto en movimiento: crea un tablón de mensajes para una tienda, un aula o la puerta de tu cuarto y cambia el mensaje desde el móvil con un módulo Bluetooth HC-05.
  • Reloj, calendario y temperatura: muestra por turnos la hora y la fecha del RTC y la temperatura y la humedad de un sensor DHT11.
  • Reloj por internet y tiempo: desplaza con un ESP32 la hora NTP y la temperatura obtenida de una API del tiempo.
  • Marcador: controla una pantalla de dos zonas para un partido de ping-pong o baloncesto con un mando IR.
  • Minijuegos: juega a la serpiente, al Pong o a esquivar obstáculos en la pantalla de 32x8 con un joystick; tienes más ideas en Proyectos de juegos con Arduino.
  • Medidor de nivel de sonido: dibuja las lecturas de un sensor de sonido como un gráfico de barras de 32 columnas.
  • Panel de animaciones de 32x32: apila cuatro módulos 4 en 1 y reproduce animaciones como lluvia, estrellas o fuegos artificiales.

Errores comunes y cómo solucionarlos

SíntomaCausa probableSolución
No se enciende ningún LEDNo hay alimentación, los cables están en el lado OUT o el pin CS no coincide con el códigoConecta los cables al lado IN marcado como DIN y revisa CS_PIN y la alimentación de 5 V
Todos los LED están al máximo y no cambianNo llegan datos: DIN y CLK están intercambiados o el chip se quedó en modo de pruebaConecta DIN a MOSI y CLK a SCK, y desconecta y vuelve a conectar la alimentación
Las letras salen en espejo, boca abajo o giradas 90 gradosHARDWARE_TYPE incorrectoPrueba FC16_HW en el módulo 4 en 1 y GENERIC_HW en el módulo individual
Todo el texto está boca abajo y se desplaza de izquierda a derechaEl módulo está montado al revés, con la entrada DIN a la izquierdaGira el módulo 180 grados; la entrada DIN debe quedar a la derecha
Solo funciona un bloque o el texto aparece cortadoMAX_DEVICES incorrecto o conexión rota entre módulosPon en MAX_DEVICES el número de bloques y revisa los cables OUT → IN
El Arduino se reinicia o la imagen parpadea al subir el brilloSe supera el límite de 500 mA del USB y cae la tensiónUsa una fuente externa de 5 V y 2 A, une los GND y baja el brillo
Puntos al azar y caracteres corruptos con el ESP32La señal de 3,3 V está por debajo del umbral HIGH de 3,5 V y los cables son largosEleva el nivel con un 74HCT125 y acorta los cables
Las tildes aparecen como símbolos sin sentidoLos bytes UTF-8 llegan tal cual a la fuenteUsa el método utf8ToFont() y addChar()
El texto avanza a tironesHay un delay() o una tarea larga dentro de loop()Llama a displayAnimate() a menudo y gestiona las esperas con millis()
Cuando algo no funcione, empieza por la prueba más sencilla: deja en el código solo display.begin() y display.print("8888"). Si no se enciende ningún LED, el problema está en el cableado o en la alimentación; comprueba con el multímetro que hay 4,5–5 V entre los pines VCC y GND del módulo. Si los LED se encienden pero los dígitos salen deformados, el problema es el tipo de hardware; si solo funciona el primer bloque, revisa MAX_DEVICES o las conexiones entre módulos.

El MAX7219 es una de las formas más baratas de conseguir una pantalla llamativa con unos pocos cables: el chip se encarga del barrido, de limitar la corriente y del brillo, y tú solo te ocupas de qué mostrar. Empieza dibujando iconos en un módulo 8x8 y pasa después al texto en movimiento, a un reloj y a mensajes inalámbricos con un ESP32 en un módulo 4 en 1. Si quieres LED de colores controlados uno a uno, los WS2812B NeoPixel son una buena opción, y para un proyecto que solo muestre números, un display de 7 segmentos. En la app BlueGrays encontrarás proyectos con el MAX7219, como un reloj y calendario de matriz de puntos, una estación meteorológica, animaciones de 32x8 y texto en movimiento, paso a paso con esquemas y código.

Preguntas frecuentes

¿Por qué el texto de mi módulo MAX7219 sale en espejo o al revés?

Cada fabricante conecta la matriz 8x8 al MAX7219 de una forma distinta, y la librería lo compensa con el ajuste HARDWARE_TYPE. La mayoría de los módulos 4 en 1 azules que se venden hoy se ven bien con FC16_HW, y los módulos individuales verdes con GENERIC_HW. Si la imagen sigue mal, prueba también PAROLA_HW e ICSTATION_HW; en el código solo cambia una línea.

¿Cuántos módulos MAX7219 se pueden encadenar?

El chip no tiene límite y, como MD_MAX72XX guarda el número de módulos en un byte, la librería admite hasta 255. En la práctica el límite lo ponen la alimentación y la calidad de la señal: cada 4–8 bloques vuelve a llevar 5 V y GND a la cadena, usa cables cortos y añade a cada módulo un condensador de 10 µF y otro de 100 nF. Cuanto más larga es la cadena, más tarda cada actualización y más RAM ocupa.

¿Funciona el MAX7219 con un ESP32 a 3,3 V?

El MAX7219 necesita al menos 4 V de alimentación, así que alimenta el módulo desde el pin 5V (VIN) del ESP32. La hoja de datos pide 3,5 V como nivel HIGH con alimentación de 5 V; las señales de 3,3 V del ESP32 funcionan igualmente en la mayoría de los módulos, pero están por debajo de ese límite. Para un funcionamiento fiable, coloca un elevador de nivel como el 74HCT125 en las líneas DIN, CLK y CS.

¿Cuánta corriente consume una matriz LED MAX7219?

Un módulo 8x8 con todos los LED encendidos al máximo brillo consume unos 330 mA, y un módulo 4 en 1 de 8x32 hasta 1,3 A. Con texto en movimiento solo se enciende parte de los LED y el brillo suele estar entre 2 y 7, así que un módulo 4 en 1 consume normalmente unos 100–250 mA. En modo apagado cada chip gasta solo unos 150 µA.

¿Cómo mostrar la ñ y las tildes en una matriz MAX7219?

La fuente por defecto de MD_Parola incluye la ñ, las vocales acentuadas y el resto de caracteres Latin-1, pero el Arduino IDE los guarda como dos bytes UTF-8. Convierte el texto a los códigos de la fuente con una pequeña función antes de mostrarlo. Los caracteres que no están en Latin-1 puedes dibujarlos tú y añadirlos con addChar().

¿Se puede controlar un display de 7 segmentos con el MAX7219?

Sí. El MAX7219 se diseñó en realidad para displays de 7 segmentos de cátodo común de hasta 8 dígitos, y tiene un modo BCD que decodifica los números por sí solo. Puedes usar fácilmente los módulos de 8 dígitos con MAX7219 con la función setDigit() de la librería LedControl. Los displays de ánodo común, en cambio, no se pueden conectar directamente a este chip.

Ver todos los proyectos
Reloj y calendario de matriz de puntos (MAX7219, DS1307)
#84Intermedio
Premium

Reloj y calendario de matriz de puntos (MAX7219, DS1307)

Un panel luminoso para tu mesa o tu pared que muestra la hora y la fecha con texto en movimiento. En la pantalla LED de 8x32 se ve un reloj de números grandes cuyos dos puntos parpadean cada segundo;

5.0(37)
58
Reloj matricial, calendario y estación meteorológica (MAX7219, DS1307)
#85Intermedio
Premium

Reloj matricial, calendario y estación meteorológica (MAX7219, DS1307)

Un panel de texto en movimiento para tu mesa o tu pared que muestra por turnos la hora, la fecha y el clima de la habitación. En la pantalla LED de 8x32 aparece primero la temperatura en grados Celsiu

5.0(50)
66
4x 32x8 Matriz LED Animación Y Texto
#90Intermedio
Premium

4x 32x8 Matriz LED Animación Y Texto

Si apilas cuatro módulos largos de matriz LED, obtienes una pantalla cuadrada con más de mil puntos de luz. Primero caen letras desde arriba como una lluvia de película, luego se abre un telón y en do

4.6(40)
66
Matriz 4x 32x8 LED - Pack de 6 animaciones
#91Intermedio
Premium

Matriz 4x 32x8 LED - Pack de 6 animaciones

Cuatro paneles LED de 32x8 colocados uno encima de otro forman una pantalla cuadrada donde se suceden seis animaciones: lluvia digital que cae, una espiral que crece, células que nacen y mueren por sí

4.5(35)
48
Serpiente de píxeles Matriz 8x8
#88Avanzado
Premium

Serpiente de píxeles Matriz 8x8

Monta con tus propias manos el clásico juego de la serpiente y juega. En una matriz de luces de colores de 8x8 guías una serpiente verde con un joystick y te comes manzanas rojas; con cada manzana la

4.7(39)
57
ESP32: hora real desde Internet con NTP
#128Avanzado
Premium

ESP32: hora real desde Internet con NTP

El ESP32 se conecta a Wi-Fi y obtiene la hora real con precisión de milisegundos desde servidores NTP (Network Time Protocol). Gracias a `configTzTime()`, se aplica automáticamente la zona horaria de Turquía (TRT, UTC+3) y el cambio DST se gestiona mediante la cadena POSIX TZ. La hora se muestra en el monitor serie y en una pantalla LCD I2C opcional. `configTzTime()` inicia el servicio SNTP; se realizan resincronizaciones periódicas mientras hay acceso a la red, y el reloj del sistema sigue avan

4.5(13)
21
Reloj digital con ESP32 y RTC DS1307 (I2C + LCD)
#120Intermedio
Premium

Reloj digital con ESP32 y RTC DS1307 (I2C + LCD)

En este proyecto, mostramos la hora y la fecha en una pantalla LCD I2C de 16x2 mediante el módulo de reloj en tiempo real (RTC) DS1307. El DS1307 es un RTC con batería de respaldo que conserva la hora incluso si el ESP32 se apaga. Ambos módulos comparten la misma línea I2C (SDA/SCL), por lo que aprendemos a controlar varios dispositivos con solo dos cables de señal. Con este proyecto conocerás el protocolo I2C, el funcionamiento de los módulos RTC y la visualización dinámica en pantallas LCD.

4.6(26)
40
Reloj digital con display de 7 segmentos de 4 dígitos
#48Intermedio
Premium

Reloj digital con display de 7 segmentos de 4 dígitos

Un reloj digital luminoso de cuatro cifras, parecido al del horno de la cocina. El módulo de reloj DS1302 guarda la hora y cada segundo el Arduino lee las horas y los minutos y los muestra en el indic

4.8(56)
73