Matrice LED MAX7219 con Arduino: testo scorrevole e orologio
Matrice LED MAX7219 con Arduino: collegamento 8x8 e 4 in 1 8x32, testo scorrevole con MD_Parola, testo a specchio risolto con FC16_HW, orologio ed ESP32.
Il MAX7219 è un driver economico che pilota da solo 64 LED ed è il modo più semplice per usare una matrice LED con Arduino. Lo trovi soprattutto in due versioni: un modulo con una sola matrice a punti 8x8 e un modulo 4 in 1 da 8x32 punti con quattro matrici affiancate. Il MAX7219 riceve da Arduino solo 3 fili di segnale (DIN, CS, CLK); si occupa da solo di scansionare le righe centinaia di volte al secondo, di regolare la luminosità e di ricordare lo stato di ogni LED. Così Arduino resta libero per testo scorrevole, orologi, tabelloni segnapunti o animazioni, e puoi allungare il display quanto vuoi collegando i moduli uno dopo l'altro.
In questo articolo imparerai come funziona il MAX7219, come collegare il modulo in SPI ad Arduino Uno, ESP32 e Raspberry Pi Pico, come mettere in cascata i moduli e come mostrare il tuo primo testo scorrevole con la libreria MD_Parola. Poi risolverai il problema del testo a specchio o capovolto, quello che blocca più spesso i principianti, scegliendo il tipo di hardware giusto (FC16_HW); calcolerai luminosità e consumo, mostrerai lettere accentate e caratteri personalizzati e costruirai un orologio con un modulo RTC e un'animazione pixel per pixel. Le ultime sezioni spiegano come inviare messaggi dal telefono con un ESP32, il codice MicroPython per il Pico e le soluzioni agli errori più comuni.

Che cos'è il MAX7219 e come funziona?
Non è possibile collegare 64 LED uno per uno ai pin di Arduino, per questo le matrici LED usano il multiplexing. In una matrice 8x8 i LED sono collegati a una griglia di 8 righe e 8 colonne. In ogni istante il MAX7219 attiva un solo gruppo di LED (un "digit" nel datasheet, cioè una riga o una colonna della matrice), indica attraverso le altre linee quali degli otto LED di quel gruppo devono accendersi e poi passa al gruppo successivo. Tutti gli otto gruppi vengono scansionati circa 800 volte al secondo; l'occhio non riesce a seguire questa velocità, quindi l'immagine appare fissa. Nel frattempo il chip conserva lo schema di ogni gruppo nella propria RAM da 8x8 bit, limita la corrente dei LED con un'unica resistenza e regola la luminosità su 16 livelli.
Arduino comunica con il MAX7219 tramite un'interfaccia seriale a 3 fili che funziona in modo simile all'SPI. Ogni comando è un pacchetto da 16 bit: il byte alto indica in quale registro scrivere e il byte basso contiene il valore. Finché CS (LOAD nel datasheet) è LOW, i bit entrano da DIN a ogni fronte di salita di CLK; quando CS torna HIGH, il pacchetto viene scritto nel registro. È la stessa idea dello shift register 74HC595, ma dato che il MAX7219 si occupa anche della scansione e della limitazione della corrente, richiede molto meno lavoro. Il funzionamento generale dell'SPI lo trovi nell'articolo Che cos'è l'SPI?.
| Caratteristica | Valore |
|---|---|
| Tensione di alimentazione | 4,0–5,5 V (in pratica 5 V) |
| Interfaccia | Seriale a 3 fili (DIN, CLK, CS), fino a 10 MHz |
| LED pilotati | 64 (una matrice 8x8 o 8 cifre a 7 segmenti) |
| Frequenza di scansione | 800 Hz tipici (8 cifre) |
| Corrente per segmento | Impostata dalla resistenza RSET, massimo 40 mA (sui moduli di solito 10 kΩ) |
| Luminosità | 16 livelli, duty cycle da 1/32 a 31/32 |
| Corrente in shutdown | Circa 150 µA |
| Soglia HIGH in ingresso | Almeno 3,5 V (con alimentazione a 5 V) |
| Collegamento in cascata | Uscita DOUT verso l'ingresso DIN del chip successivo |
Il MAX7219 ha 14 registri indirizzabili, ma quelli che ti servono per una matrice LED sono nella tabella qui sotto. Le librerie li impostano al posto tuo; sapere a cosa servono, però, aiuta molto quando cerchi un guasto. Ad esempio, alla prima accensione il chip parte in modalità shutdown con il display spento: se il codice di inizializzazione non viene eseguito, il modulo sembra completamente morto. Il fratello MAX7221 ha gli stessi registri, ma aggiunge un vero pin CS compatibile SPI e driver dei segmenti a pendenza limitata che riducono le interferenze elettromagnetiche.
| Indirizzo | Registro | Funzione |
|---|---|---|
| 0x00 | No-Op | Lascia invariato questo chip mentre un pacchetto va a un altro chip della catena |
| 0x01–0x08 | Digit 0–7 | Lo schema a 8 bit di ciascuna delle otto righe (o colonne) |
| 0x09 | Decode Mode | Decodifica BCD per i 7 segmenti; disattivata (0x00) per una matrice |
| 0x0A | Intensity | Luminosità, da 0x0 a 0xF |
| 0x0B | Scan Limit | Numero di cifre da scansionare; 7 (tutte e otto) per una matrice |
| 0x0C | Shutdown | 0: modalità shutdown, 1: funzionamento normale |
| 0x0F | Display Test | 1: tutti i LED si accendono alla massima luminosità |
Modulo 8x8 singolo o modulo 4 in 1 da 8x32?
I due moduli che incontrerai più spesso usano lo stesso circuito; la differenza sta in come la matrice LED è collegata al MAX7219 e nella forma della scheda. Il modulo singolo su scheda verde monta un MAX7219 in contenitore DIP (24 pin) e una matrice 8x8 come la 1088AS, con i pin di ingresso (VCC, GND, DIN, CS, CLK) a un'estremità e quelli di uscita (VCC, GND, DOUT, CS, CLK) all'altra. Sul modulo 4 in 1 a scheda blu, quattro MAX7219 SMD si trovano sotto le matrici e i quattro blocchi sono già collegati in cascata in fabbrica; sulla scheda di solito c'è la sigla FC-16. Per un testo scorrevole lungo, il modulo 4 in 1 è più economico e più ordinato.

| Caratteristica | Modulo 8x8 singolo | Modulo 4 in 1 8x32 (FC-16) |
|---|---|---|
| Risoluzione | 8x8 = 64 LED | 32x8 = 256 LED |
| Numero di MAX7219 | 1 (contenitore DIP) | 4 (contenitore SMD) |
| Tipo di hardware nel codice | Di solito GENERIC_HW | Di solito FC16_HW |
| Caratteri visibili insieme | 1 | 4–6 (secondo la larghezza delle lettere) |
| Corrente massima alla massima luminosità | Circa 330 mA | Circa 1,3 A |
| Uso ideale | Icone, dado, faccine, piccoli giochi | Testo scorrevole, orologi, segnapunti |
Collegamento del MAX7219: Arduino Uno, ESP32 e Pico
Il modulo ha cinque pin: VCC e GND per l'alimentazione, DIN per i dati, CLK per il clock e CS per la selezione del chip (caricamento). Per impostazione predefinita MD_Parola e MD_MAX72XX usano l'SPI hardware: in questo caso DIN deve andare al pin MOSI della scheda e CLK al pin SCK, mentre per CS puoi scegliere qualsiasi pin digitale. Puoi controllare dove si trovano i pin SPI hardware della tua scheda con lo strumento Pinout. Collega sempre i fili al lato di ingresso (IN) del modulo, l'estremità con la scritta DIN; l'estremità con DOUT serve per il modulo successivo.
| Pin del modulo | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| VCC | 5V | 5V (VIN) | VBUS (5 V) |
| GND | GND | GND | GND |
| DIN | D11 (MOSI) | GPIO 23 (MOSI) | GP19 (SPI0 TX) |
| CS | D10 | GPIO 5 | GP17 |
| CLK | D13 (SCK) | GPIO 18 (SCK) | GP18 (SPI0 SCK) |
I pin di ESP32 e Pico lavorano a 3,3 V, mentre il datasheet del MAX7219 richiede almeno 3,5 V come livello HIGH con alimentazione a 5 V. I segnali a 3,3 V sono quindi tecnicamente appena sotto il limite. La maggior parte dei moduli funziona lo stesso, ma con fili lunghi o con certi lotti di chip possono comparire punti accesi a caso e immagini disturbate. La soluzione affidabile è un buffer come il 74HCT125, che accetta 3,3 V come HIGH e fornisce 5 V in uscita, oppure un convertitore di livello logico veloce sulle linee DIN, CLK e CS. Anche tenere i fili di segnale sotto i 20 cm spesso risolve il problema.
Collegare i moduli in cascata (daisy chain)
Il pin DOUT del MAX7219 restituisce i dati entrati da DIN 16,5 cicli di clock dopo. Quando colleghi il lato OUT di un modulo al lato IN del successivo, DOUT va al DIN del chip seguente, mentre CLK e CS sono in comune a tutti i chip. Così da Arduino escono sempre solo 3 fili di segnale, ma il display può essere lungo 8, 16 o 32 blocchi. A ogni aggiornamento la libreria invia i pacchetti lungo tutta la catena e, quando CS sale, tutti si aggiornano nello stesso istante. Sui moduli 4 in 1 questo collegamento è già presente sulla scheda; per ottenere un display da 8 blocchi (64x8) con due moduli 4 in 1, basta collegare i pin OUT del primo ai pin IN del secondo e impostare MAX_DEVICES a 8 nel codice.
Nella libreria la numerazione dei blocchi parte dal modulo più vicino all'ingresso DIN e cresce verso sinistra; per MD_MAX72XX l'origine delle coordinate è l'angolo in alto a destra del display. Per questo l'ingresso DIN deve restare sul lato destro, perché il testo si legga correttamente. Più la catena si allunga, più l'alimentazione diventa il vero limite: ogni 4–8 blocchi riporta 5 V e GND nella catena con fili spessi e aggiungi all'ingresso di alimentazione di ogni modulo un condensatore elettrolitico da 10 µF e uno ceramico da 100 nF. Anche il datasheet consiglia questa coppia per livellare l'ondulazione causata dai picchi di corrente dei LED.
Installare le librerie MD_MAX72XX e MD_Parola
Le due librerie più usate per il MAX7219 sono dello stesso autore (majicDesigns) e lavorano insieme. MD_MAX72XX fa il lavoro a basso livello: inizializza i moduli, scrive pixel e colonne e gestisce il font. MD_Parola si appoggia a essa e aggiunge testo scorrevole, effetti, allineamento e zone. Anche la libreria LedControl che vedi negli esempi più vecchi funziona, ma lo scorrimento del testo devi programmarlo tu. Se non hai mai installato una libreria, leggi prima Come aggiungere una libreria ad Arduino.
- Nell'Arduino IDE apri il Gestore librerie (Strumenti > Gestione librerie) e cerca MD_Parola.
- Installa la libreria MD_Parola di majicDesigns. Quando ti chiede delle dipendenze, scegli Install all così viene installata anche MD_MAX72XX.
- Collega il modulo come nella tabella sopra e, per un modulo 4 in 1, imposta nel codice HARDWARE_TYPE su FC16_HW e MAX_DEVICES su 4.
- Carica lo sketch. Se il testo appare capovolto, a specchio o ruotato, niente paura: lo correggerai con una sola riga nella sezione sul tipo di hardware.
Primo programma: testo scorrevole con MD_Parola
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 riga più importante è displayText(): riceve, in quest'ordine, il testo, l'allineamento (PA_LEFT, PA_CENTER, PA_RIGHT), il tempo tra un fotogramma e l'altro in millisecondi, la pausa quando il testo è arrivato in posizione e gli effetti di entrata e di uscita. Più il valore di velocità è piccolo, più il testo scorre veloce; tra 25 e 60 ms si ottiene uno scorrimento ben leggibile. displayAnimate() avanza al massimo di un fotogramma a ogni chiamata e ritorna subito. Per questo va chiamata di continuo in loop() e nel resto del codice non devi usare delay(), altrimenti il testo avanza a scatti. Come aspettare senza bloccare il programma lo trovi nell'articolo millis() e delay().
| Funzione | Cosa fa |
|---|---|
| begin() | Inizializza i moduli, li fa uscire dallo shutdown e pulisce il display |
| displayText(testo, allineamento, velocità, pausa, entrata, uscita) | Imposta il testo e gli effetti da mostrare e avvia l'animazione |
| displayAnimate() | Disegna il fotogramma successivo; restituisce true quando l'effetto è finito |
| displayReset() | Riproduce da capo lo stesso testo e lo stesso effetto |
| print("...") | Mostra un testo fisso senza effetti |
| setIntensity(0–15) | Regola la luminosità di tutti i moduli |
| setInvert(true) | Scambia LED accesi e spenti (immagine in negativo) |
| displayShutdown(true) | Mette il display in shutdown; con false lo riaccende |
| begin(2) e setZone(0, 0, 1) | Divide il display in zone indipendenti, ognuna con il suo testo e il suo effetto |
Oltre a PA_SCROLL_LEFT ci sono molti effetti di entrata e di uscita: PA_PRINT (il testo appare di colpo), PA_SCROLL_UP e PA_SCROLL_DOWN (scorrimento verticale), PA_WIPE (tendina), PA_OPENING e PA_CLOSING (apertura e chiusura dal centro), PA_FADE (dissolvenza con la luminosità), PA_DISSOLVE, PA_BLINDS, PA_RANDOM e PA_GROW_UP. Per vederli tutti carica l'esempio Parola_Animation_Catalog da File > Esempi > MD_Parola; ricorda di adattare le righe HARDWARE_TYPE e MAX_DEVICES al tuo modulo.
Scegliere il tipo di hardware (FC16_HW) e correggere il testo a specchio
I produttori collegano la matrice 8x8 al MAX7219 in modi diversi: su alcune schede le linee "digit" del chip vanno alle righe, su altre alle colonne, e l'ordine di righe o colonne può essere invertito. La libreria compensa questa differenza con l'impostazione HARDWARE_TYPE. Se scegli il tipo sbagliato il modulo non è guasto, ma le lettere appaiono a specchio, capovolte o ruotate di 90 gradi in ogni blocco 8x8. Dato che molti codici vecchi che trovi online usano PAROLA_HW o GENERIC_HW, è il problema più frequente con i moduli 4 in 1 blu venduti oggi. La tabella indica cosa cambiare in base al sintomo; si riferisce al modulo 4 in 1 FC-16.
| Cosa vedi | Causa probabile | Soluzione |
|---|---|---|
| Le lettere sono a specchio (destra-sinistra) in ogni blocco | PAROLA_HW selezionato, ordine delle colonne invertito | Usa FC16_HW |
| Le lettere sono ruotate di 90 gradi in ogni blocco | GENERIC_HW selezionato, righe e colonne scambiate | Usa FC16_HW |
| Le lettere sono capovolte in ogni blocco | ICSTATION_HW selezionato, ordine di righe e colonne invertito | Usa FC16_HW |
| Tutto il testo è capovolto e scorre da sinistra a destra | Il modulo è girato, con l'ingresso DIN a sinistra | Ruota il modulo di 180 gradi in modo che l'ingresso DIN sia a destra |
| Solo alcuni blocchi sono corretti, gli altri sono vuoti o confusi | MAX_DEVICES non corrisponde al numero reale di blocchi | Imposta MAX_DEVICES al numero reale di blocchi |
Regolazione della luminosità e consumo
La luminosità dipende da due cose. La prima è la resistenza RSET sul modulo: il chip fornisce ai LED circa 100 volte la corrente che esce dal pin ISET. Sui moduli si usano di solito 10 kΩ, cioè una corrente di picco poco sopra i 40 mA con LED rossi e di circa 40 mA con LED blu e bianchi. La seconda è il registro Intensity che imposti via software: un valore n da 0 a 15 tiene accesi i LED per (2n+1)/32 di ogni intervallo di scansione, quindi 0 è la luminosità minima (1/32) e 15 la massima (31/32). Inoltre, dato che ogni riga è accesa solo per un ottavo del tempo, anche alla massima luminosità un singolo LED assorbe in media circa 5 mA. Le tensioni dirette in base al colore del LED le trovi nello strumento Tensioni dei LED.
Puoi stimare in modo approssimativo la corrente media di un modulo così: I ≈ 8 mA + 5 mA × (2n+1)/32 × L. Qui 8 mA è il consumo proprio del chip, n il valore di luminosità e L il numero di LED accesi su quel modulo (0–64); 5 mA è un ottavo della corrente di picco di 40 mA. Ad esempio, con luminosità 15 e tutti i 64 LED accesi si ottiene 8 + 5 × 0,97 × 64 ≈ 318 mA, in linea con i 330 mA del datasheet. Con un testo scorrevole di solito si accende solo il 25–35 per cento dei LED, quindi il consumo reale è molto più basso. La tabella mostra valori tipici per un modulo 4 in 1.
| Situazione | Per modulo | Modulo 4 in 1 (32x8) |
|---|---|---|
| Tutti i LED accesi, luminosità 15 | ≈ 320 mA | ≈ 1,3 A |
| Tutti i LED accesi, luminosità 7 | ≈ 160 mA | ≈ 630 mA |
| Testo scorrevole (~30% dei LED), luminosità 7 | ≈ 55 mA | ≈ 210 mA |
| Testo scorrevole (~30% dei LED), luminosità 2 | ≈ 25 mA | ≈ 90 mA |
| Modalità shutdown (displayShutdown) | ≈ 0,15 mA | ≈ 0,6 mA |
Per abbassare il display di notte puoi regolare la luminosità in automatico con un sensore di luce LDR e mettere in shutdown il display con displayShutdown(true) quando non c'è nessuno. Se stai progettando un'insegna a batteria, inserisci i valori sopra nello strumento Durata della batteria per calcolare quante ore resisterà. In interni una luminosità di 2–4 invece di 15 di solito basta: affatica meno gli occhi e riduce la corrente dei LED a circa un terzo, fino a un sesto.
Lettere accentate e caratteri speciali (à, è, é, ò, ù)
L'Arduino IDE salva il codice in UTF-8, quindi una lettera come "è" o "à" nel testo non occupa un byte, ma due. MD_Parola cerca ogni byte come numero di carattere nel font, per questo le lettere accentate diventano simboli senza senso. Il font predefinito della libreria contiene tutto il set Latin-1 (à, è, é, ì, ò, ù e altri), quindi basta una piccola funzione che converta l'UTF-8 nei codici del font. Le lettere che non fanno parte del Latin-1, come le turche ğ, ı e ş dell'esempio qui sotto, vanno disegnate pixel per pixel e aggiunte con addChar(). Lo sketch fa entrambe le cose.
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}In ogni array di carattere personalizzato il primo byte indica la larghezza della lettera in colonne e i byte successivi sono le colonne da sinistra a destra; il bit 0 di ogni byte è la riga più in alto. Ad esempio la ı turca (i senza puntino) è una sola colonna e il valore 124 (0b01111100) accende le righe da 2 a 6; si distingue dalla i normale solo per il puntino mancante. Nelle maiuscole Ğ e İ la lettera è stata accorciata di due righe (5 invece di 7) per lasciare spazio al segno sopra. addChar() non copia i dati, ne memorizza solo l'indirizzo: gli array devono quindi essere globali e aggiunti dopo begin(). Sono stati scelti i codici 0x80–0x85 perché non compaiono mai in un testo normale. Allo stesso modo puoi aggiungere qualsiasi simbolo, come un cuore o una freccia.
Esempio di orologio: orologio digitale con modulo RTC

Il contatore millis() di Arduino si azzera quando manca la corrente e, a causa del risonatore ceramico della Uno, può sbagliare anche di alcuni minuti al giorno. Per un vero orologio usa un modulo orologio RTC. Collega il modulo DS1307 al bus I2C (SDA → A4, SCL → A5, VCC → 5V, GND → GND); il MAX7219 usa i pin SPI, quindi i due moduli non si disturbano. Per il codice installa dal Gestore librerie la libreria RTClib di Adafruit.
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}Lo sketch legge l'ora una volta al secondo e fa lampeggiare i due punti. Nel font predefinito i due punti (:) e lo spazio hanno la stessa larghezza (2 colonne), quindi le cifre non si spostano. Al primo avvio l'RTC viene impostato sul momento in cui lo sketch è stato compilato. Un DS1307 può sbagliare di circa un minuto al mese; per un orologio più preciso prendi un modulo DS3231 e nel codice usa RTC_DS3231 al posto di RTC_DS1307 e sostituisci la condizione if (!rtc.isrunning()) con if (rtc.lostPower()). Grazie al quarzo compensato in temperatura, il DS3231 sbaglia solo di circa un minuto all'anno. Puoi anche dividere il display in due zone per mostrare l'ora da un lato e la temperatura dall'altro.
Animazione: disegnare pixel per pixel con MD_MAX72XX
Per andare oltre il testo scorrevole puoi usare direttamente la libreria MD_MAX72XX. Tratta l'intera catena come un unico display a pixel: un modulo 4 in 1 ha 32 colonne e 8 righe. setPoint(riga, colonna, stato) accende o spegne un singolo LED, setColumn() scrive in un colpo solo gli 8 bit di una colonna e clear() pulisce il display. L'origine (0, 0) è l'angolo in alto a destra; i numeri di colonna crescono verso sinistra e quelli di riga verso il basso. Il codice seguente disegna una pallina che rimbalza sui bordi del display:
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}In ogni fotogramma si disattiva prima l'aggiornamento automatico (UPDATE), si cancella il vecchio pixel e si disegna quello nuovo, poi si riattiva UPDATE in modo che tutte le modifiche arrivino ai moduli in un colpo solo. Così non compare mai un fotogramma disegnato a metà. Con lo stesso metodo puoi disegnare un dado, faccine, il gioco del serpente o barre del livello sonoro. Gli esempi di MD_MAX72XX includono anche animazioni pronte come Pac-Man, occhi da robot e una clessidra. Se usi MD_Parola, raggiungi le stesse funzioni di disegno con getGraphicObject().
Messaggi via internet con ESP32: testo scorrevole dal telefono
Con un ESP32 puoi rendere wireless la tua insegna a testo scorrevole. Il codice qui sotto si collega alla rete di casa, fa scorrere prima il proprio indirizzo IP e poi mostra l'ora presa da internet (NTP). Apri quell'indirizzo IP nel browser di un telefono collegato allo stesso Wi-Fi e vedrai una casella di testo; il messaggio che scrivi inizia a scorrere al giro successivo e si alterna con l'orologio. Il modulo si collega ai pin VSPI come nella tabella: CLK al GPIO 18, DIN al GPIO 23 e CS al GPIO 5. Se sei alle prime armi con l'ESP32, leggi prima Che cos'è l'ESP32?.
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() non manda subito sul display il messaggio arrivato dal modulo web, ma lo mette in attesa in nextMessage. Il messaggio viene copiato solo quando l'animazione in corso è finita (quando displayAnimate() restituisce true); così il testo che MD_Parola sta leggendo non cambia mai a metà. Il valore 3 × 3600 in configTime() è il fuso orario UTC+3 della Turchia; in Italia sostituisci quella riga con configTzTime("CET-1CEST,M3.5.0,M10.5.0/3", "pool.ntp.org"), che gestisce anche l'ora legale. Per messaggi con lettere accentate aggiungi a questo sketch le righe addChar() e la funzione utf8ToFont() viste sopra, e sostituisci la riga con strlcpy() con utf8ToFont(nextMessage, message, sizeof(message)). Per l'ora da internet e i pannelli web in tempo reale guarda Orologio NTP con ESP32 e Web server e WebSocket con ESP32.
MAX7219 con Raspberry Pi Pico e MicroPython
Sul Pico con MicroPython la strada più pratica è copiare sul Pico con Thonny il driver open source max7219.py (micropython-max7219). Poiché il driver usa il modulo framebuf di MicroPython, funzioni di disegno come text(), pixel(), line() e rect() sono già pronte, e sui moduli 4 in 1 FC-16 l'orientamento è corretto. Alimenta il modulo da VBUS (5 V) e collega DIN a GP19, CLK a GP18 e CS a GP17. MicroPython riserva anche il pin MISO di SPI0 (GP16), quindi non usarlo per altro. Il font di framebuf è da 8x8 pixel e solo ASCII, per cui un modulo 4 in 1 mostra 4 lettere alla volta e niente lettere accentate. Per la configurazione vedi Che cos'è il Raspberry Pi Pico? e Basi di 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)Idee di progetti con il MAX7219
- Insegna a testo scorrevole: crea una bacheca per un negozio, un'aula o la porta della tua camera e cambia il messaggio dal telefono con un modulo Bluetooth HC-05.
- Orologio, calendario e temperatura: mostra a turno ora e data dall'RTC e temperatura e umidità da un sensore DHT11.
- Orologio internet e meteo: fai scorrere con un ESP32 l'ora NTP e la temperatura presa da un'API meteo.
- Segnapunti: gestisci un display a due zone per una partita di ping pong o di basket con un telecomando IR.
- Minigiochi: gioca a snake, a Pong o a schivare ostacoli sul display 32x8 con un joystick; altre idee in Progetti di giochi con Arduino.
- Indicatore di livello sonoro: disegna le letture di un sensore sonoro come un grafico a barre da 32 colonne.
- Pannello di animazioni 32x32: impila quattro moduli 4 in 1 e riproduci animazioni come pioggia, stelle o fuochi d'artificio.
Errori comuni e soluzioni
| Sintomo | Causa probabile | Soluzione |
|---|---|---|
| Non si accende nessun LED | Manca l'alimentazione, i fili sono sul lato OUT o il pin CS non corrisponde al codice | Collega i fili al lato IN con la scritta DIN, controlla CS_PIN e l'alimentazione a 5 V |
| Tutti i LED sono accesi al massimo e non cambiano | Non arrivano dati: DIN e CLK sono scambiati o il chip è rimasto in modalità test | Collega DIN a MOSI e CLK a SCK, poi togli e ridai l'alimentazione |
| Lettere a specchio, capovolte o ruotate di 90 gradi | HARDWARE_TYPE sbagliato | Prova FC16_HW sul modulo 4 in 1 e GENERIC_HW sul modulo singolo |
| Tutto il testo è capovolto e scorre da sinistra a destra | Modulo montato al contrario, ingresso DIN a sinistra | Ruota il modulo di 180 gradi; l'ingresso DIN deve essere a destra |
| Funziona solo un blocco o il testo appare tagliato | MAX_DEVICES sbagliato o collegamento interrotto tra i moduli | Imposta MAX_DEVICES al numero di blocchi e controlla i fili OUT → IN |
| Arduino si riavvia o l'immagine sfarfalla ad alta luminosità | Si supera il limite USB di 500 mA e la tensione cala | Usa un alimentatore esterno da 5 V 2 A, unisci i GND e abbassa la luminosità |
| Punti casuali e caratteri disturbati con l'ESP32 | Il segnale a 3,3 V è sotto la soglia HIGH di 3,5 V e i fili sono lunghi | Alza il livello con un 74HCT125 e accorcia i fili |
| Le lettere accentate diventano simboli senza senso | I byte UTF-8 arrivano così come sono al font | Usa il metodo utf8ToFont() e addChar() |
| Il testo scorrevole avanza a scatti | In loop() c'è un delay() o un'operazione lunga | Chiama spesso displayAnimate() e gestisci le attese con millis() |
Il MAX7219 è uno dei modi più economici per ottenere un display d'effetto con pochi fili: il chip si occupa di scansione, limitazione della corrente e luminosità, e tu pensi solo a cosa mostrare. Comincia disegnando icone su un modulo 8x8, poi passa al testo scorrevole, all'orologio e ai messaggi wireless con un ESP32 su un modulo 4 in 1. Se vuoi LED colorati controllabili uno per uno, i WS2812B NeoPixel sono un'ottima scelta, e per un progetto che mostra solo numeri un display a 7 segmenti. Nell'app BlueGrays trovi progetti con il MAX7219 come un orologio e calendario a matrice di punti, una stazione meteo, animazioni 32x8 e testo scorrevole, passo dopo passo con schemi e codice.
Domande frequenti
Perché il testo sul mio modulo MAX7219 è a specchio o capovolto?
I produttori collegano la matrice 8x8 al MAX7219 in modi diversi e la libreria compensa con l'impostazione HARDWARE_TYPE. La maggior parte dei moduli 4 in 1 blu venduti oggi si vede correttamente con FC16_HW, i moduli singoli verdi con GENERIC_HW. Se l'immagine è ancora sbagliata, prova anche PAROLA_HW e ICSTATION_HW; nel codice cambia una sola riga.
Quanti moduli MAX7219 si possono collegare in cascata?
Il chip in sé non ha limiti e, poiché MD_MAX72XX memorizza il numero di moduli in un byte, la libreria ne accetta fino a 255. In pratica il limite lo fissano l'alimentazione e la qualità del segnale: ogni 4–8 blocchi riporta 5 V e GND nella catena, usa fili corti e aggiungi a ogni modulo un condensatore da 10 µF e uno da 100 nF. Più la catena è lunga, più ogni aggiornamento richiede tempo e RAM.
Il MAX7219 funziona con un ESP32 a 3,3 V?
Il MAX7219 ha bisogno di almeno 4 V di alimentazione, quindi alimenta il modulo dal pin 5V (VIN) dell'ESP32. Il datasheet richiede 3,5 V come livello HIGH con alimentazione a 5 V; i segnali a 3,3 V dell'ESP32 funzionano comunque sulla maggior parte dei moduli, ma sono sotto quel limite. Per un funzionamento affidabile metti un traslatore di livello come il 74HCT125 sulle linee DIN, CLK e CS.
Quanta corrente assorbe una matrice LED MAX7219?
Un modulo 8x8 con tutti i LED accesi alla massima luminosità assorbe circa 330 mA, un modulo 4 in 1 8x32 fino a 1,3 A. Con il testo scorrevole si accende solo una parte dei LED e la luminosità di solito resta tra 2 e 7, quindi un modulo 4 in 1 assorbe in genere circa 100–250 mA. In modalità shutdown ogni chip consuma solo circa 150 µA.
Come si visualizzano le lettere accentate su una matrice MAX7219?
Il font predefinito di MD_Parola contiene à, è, é, ò, ù e gli altri caratteri Latin-1, ma l'Arduino IDE li salva come due byte UTF-8. Prima di mostrarlo, converti quindi il testo nei codici del font con una piccola funzione. I caratteri che non fanno parte del Latin-1 puoi disegnarli tu e aggiungerli con addChar().
Si può pilotare un display a 7 segmenti con il MAX7219?
Sì. Il MAX7219 è stato progettato proprio per display a 7 segmenti a catodo comune fino a 8 cifre e ha una modalità BCD che decodifica le cifre da sola. Puoi usare facilmente i moduli display MAX7219 a 8 cifre con la funzione setDigit() della libreria LedControl. I display ad anodo comune, invece, non si possono collegare direttamente a questo chip.
Progetti da realizzare dopo questo articolo
Vedi tutti i progetti
Orologio e calendario a matrice di punti (MAX7219, DS1307)
Un pannello luminoso per la scrivania o la parete che mostra ora e data con scritte scorrevoli. Sul display LED 8x32 c'è un orologio a cifre grandi con i due punti che lampeggiano ogni secondo; all'in

Orologio a matrice di punti, calendario e stazione meteo (MAX7219, DS1307)
Un pannello a scritte scorrevoli per la scrivania o la parete che mostra a turno l'ora, la data e il clima della stanza. Sul display LED 8x32 compaiono prima la temperatura in Celsius e Fahrenheit, po

Animazione e testo LED a matrice 4x 32x8
Impilando quattro lunghi moduli a matrice di LED ottieni uno schermo quadrato con più di mille punti luminosi. Prima le lettere cadono dall'alto come una pioggia da film, poi si apre un sipario e due

Matrice LED 4x 32x8 - Pacchetto di 6 animazioni
Quattro pannelli LED 32x8 messi uno sopra l'altro formano uno schermo quadrato su cui si alternano sei animazioni: pioggia digitale che cade, una spirale che cresce, cellule che nascono e muoiono da s

Serpente di pixel Matrice 8x8
Costruisci con le tue mani il classico gioco del serpente e giocaci. Su una matrice di luci colorate 8x8 guidi un serpente verde con un joystick e mangi mele rosse; a ogni mela il serpente si allunga,

ESP32: ora reale da Internet tramite NTP
L'ESP32 si connette al Wi-Fi e recupera l'ora reale con precisione al millisecondo dai server NTP (Network Time Protocol). Grazie a `configTzTime()`, il fuso orario della Turchia (TRT, UTC+3) viene applicato automaticamente e il passaggio all'ora legale è gestito tramite la stringa POSIX TZ. L'ora viene mostrata sul Serial Monitor e su un display LCD I2C opzionale. `configTzTime()` avvia il servizio SNTP; vengono eseguite risincronizzazioni periodiche finché è disponibile l'accesso alla rete, e

Orologio digitale con ESP32 e RTC DS1307 (I2C + LCD)
In questo progetto, utilizziamo il modulo orologio in tempo reale (RTC) DS1307 per visualizzare l'ora e la data su un display LCD 16x2 I2C. Il DS1307 è un RTC con batteria di supporto che mantiene l'ora anche se l'ESP32 si spegne. Entrambi i moduli condividono la stessa linea I2C (SDA/SCL), permettendoci di imparare a controllare più dispositivi utilizzando solo due cavi di segnale. Cosi imparerai il protocollo I2C e il funzionamento dei moduli RTC nella pratica.

Orologio digitale con display a 7 segmenti a 4 cifre
Un orologio digitale luminoso a quattro cifre, simile a quello del forno in cucina. Il modulo orologio DS1302 tiene l'ora, e ogni secondo l'Arduino legge ore e minuti e li mostra sul display a quattro
Altri articoli
App BlueGrays
Costruisci questo circuito passo passo sul telefono
Schema, codice ed elenco componenti in un’unica schermata. Oltre 160 progetti Arduino, ESP32 e Pico da realizzare.
Inquadra con il telefono
Punta la fotocamera sul codice: si apre lo store del tuo telefono.
oppure apri lo store

