Arduino UNO R4 - LED Matrix

This tutorial instructs you how to use Arduno UNO R4 with external LED matrix display modules. In detail, we will learn:

Then, you can simply modify the code for other LED matrices like the 16x8 or 64x8 LED matrix.

Arduino UNO R4 LED Matrix

If you want to learn about how to use Arduino R4 with on-board LED Matrix, please refer to Arduino UNO R4 - Built-in LED Matrix

Overview of LED Matrix

LED Matrix display

Various types of LED Matrix exist. The MAX7219-based LED matrix is commonly used with Arduino UNO R4. This LED matrix includes several features:

  • The basic component of an LED matrix is called a block.
  • Each block contains an 8x8 grid of LEDs, totaling 64 LEDs, and is controlled by a MAX7219 chip.
  • There are two main types of blocks: the generic module and the FC-16 module.
  • An LED matrix can have just one block or be expanded by connecting multiple blocks together like a chain.
  • You can purchase LED matrices that already have multiple blocks connected, such as 4-block or 8-block configurations.
  • Alternatively, you can buy individual blocks and connect them yourself to create an LED matrix of the size you need.
  • In your Arduino UNO R4 program, you must specify the size of the LED matrix you're using.

Pinout

LED Matrix Pinout

A LED Matrix is made up of one or more blocks. Each block has two sets of pins:

  • Group of Input Pins:
    • VCC: Link this to the 5V.
    • GND: Link this to the GND.
    • DIN: This is the Data pin. Connect it to the SPI MOSI pin on the Arduino UNO R4.
    • CS: Chip Select. Connect this to any digital pin on the Arduino UNO R4.
    • CLK: Clock pin. Connect this to the SPI CLK pin on the Arduino UNO R4.
  • Group of Output Pins:
    • VCC: Connect this to VCC on the subsequent module.
    • GND: Connect this to GND on the subsequent module.
    • DOUT: Data Out. This connects to the DIN pin on the next module.
    • CS: Connect this to CS on the subsequent module.
    • CLK: Connect this to CLK on the subsequent module.

Wiring Diagram

If the LED matrix consists of just one block:

  • Connect the groups of input pins to the Arduino UNO R4.
  • Leave the groups of output pins unconnected.
The wiring diagram between Arduino UNO R4 8x8 LED matrix FC-16

This image is created using Fritzing. Click to enlarge image

The wiring diagram between Arduino UNO R4 8x8 LED matrix generic

This image is created using Fritzing. Click to enlarge image

If the LED matrix comes already assembled into multiple blocks:

  • Connect the group of input pins to the Arduino UNO R4.
  • Leave the group of output pins unconnected.
The wiring diagram between Arduino UNO R4 LED matrix display

This image is created using Fritzing. Click to enlarge image

If you assemble the LED matrix from several parts:

  • Connect the input pin group of the first block to the Arduino UNO R4.
  • Connect the output pin group of each block to the input pin group of the following block.
  • Leave the output pin group of the last block unconnected.
Arduino UNO R4 32x8 LED matrix wiring FC-16 diagram

This image is created using Fritzing. Click to enlarge image

Arduino UNO R4 32x8 LED matrix wiring generic diagram

This image is created using Fritzing. Click to enlarge image

See The best way to supply power to the Arduino Uno R4 and other components.

The display uses a lot of power, up to 1 ampere at maximum brightness.

  • Avoid using the 5V pin on the Arduino UNO R4 for power.
  • Instead, connect an external 5V power supply. The Arduino UNO R4 and the LED matrix can both be powered by the same 5V adapter.

The Arduino UNO R4 connects to the LED matrix using SPI pins.

  • Use pin 13 (SCK) and 11 (MOSI) on Arduino UNO R4. If using a different Arduino UNO R4 board, look at the official guide for the same SPI pins.
  • You can use any other pin for Pin 3 (CS).

How To Program For LED Matrix

Controlling the LED matrix can be challenging. Luckily, there are libraries that simplify this task. Here are the steps to program an Arduino UNO R4 to control the LED matrix.

  • Include libraries:
#include <MD_Parola.h> #include <MD_MAX72xx.h>
  • Choose the type of hardware you are using: GENERIC_HW or FC16_HW.
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
  • Determine how many LED blocks are used. For instance, a 4-in-1 LED matrix contains 4 blocks.
#define MAX_DEVICES 4
  • Set the pin attached to the CS pin on the LED matrix. For example, use pin D9.
#define CS_PIN 9
  • Make a new version of the MD_Parola class for the LED matrix display.
MD_Parola ledMatrix = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
  • Set up the code in the setup() function.
void setup() { ledMatrix.begin(); // Initialize the LED matrix ledMatrix.setIntensity(0); // Set the display brightness (range 0-15) ledMatrix.displayClear(); // Clear the display screen }
  • Show text, numbers, and animated effects: see the following part.

Arduino UNO R4 - LED Matrix Code

The code provided is for a 32x8 FC-16 LED matrix display with four blocks. However, it can be modified for other sizes like 8x8, 16x8, 64x8, and so on.

/* * This Arduino UNO R4 code was developed by newbiely.com * * This Arduino UNO R4 code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/arduino-uno-r4/arduino-uno-r4-led-matrix */ #include <MD_Parola.h> #include <MD_MAX72xx.h> #define HARDWARE_TYPE MD_MAX72XX::FC16_HW #define MAX_DEVICES 4 // 4 blocks #define CS_PIN 9 // The Arduino UNO R4 pin connected to the CS pin of LED matrix // create an instance of the MD_Parola class MD_Parola ledMatrix = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES); void setup() { ledMatrix.begin(); // initialize the object ledMatrix.setIntensity(15); // set the brightness of the LED matrix display (from 0 to 15) ledMatrix.displayClear(); // clear LED matrix display } void loop() { ledMatrix.setTextAlignment(PA_LEFT); ledMatrix.print("Left"); // display text delay(2000); ledMatrix.setTextAlignment(PA_CENTER); ledMatrix.print("Center"); // display text delay(2000); ledMatrix.setTextAlignment(PA_RIGHT); ledMatrix.print("Right"); // display text delay(2000); ledMatrix.setTextAlignment(PA_CENTER); ledMatrix.setInvert(true); ledMatrix.print("Invert"); // display text inverted delay(2000); ledMatrix.setInvert(false); ledMatrix.print(1234); // display number delay(2000); }

Detailed Instructions

Follow these instructions step by step:

  • If this is your first time using the Arduino Uno R4 WiFi/Minima, refer to the tutorial on setting up the environment for Arduino Uno R4 WiFi/Minima in the Arduino IDE.
  • Connect the Arduino Uno R4 board to the led matrix according to the provided diagram.
  • Connect the Arduino Uno R4 board to your computer using a USB cable.
  • Launch the Arduino IDE on your computer.
  • Select the appropriate Arduino Uno R4 board (e.g., Arduino Uno R4 WiFi) and COM port.
  • In the Arduino IDE, click on the Libraries icon on the left sidebar.
  • Search for "MD_Parola" and locate the MD_Parola library.
  • Click the Install button.
Arduino UNO R4 MD_Parola library
  • You need to install the MD_MAX72XX library. Please click the Install All button.
Arduino UNO R4 MD_MAX72XX library
  • Copy the code above and open it using Arduino IDE.
  • Click the Upload button in Arduino IDE to upload the code to Arduino UNO R4.
  • Watch the display on the LED matrix.

Arduino UNO R4 LED Matrix Code – Scrolling Text

When you need to show a long message on a LED matrix display and it is too long to fit, you can use the scrolling text effect.

This Arduino UNO R4 code demonstrates how to make a message scroll across the LED matrix display.

/* * This Arduino UNO R4 code was developed by newbiely.com * * This Arduino UNO R4 code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/arduino-uno-r4/arduino-uno-r4-led-matrix */ #include <MD_Parola.h> #include <MD_MAX72xx.h> #define HARDWARE_TYPE MD_MAX72XX::FC16_HW #define MAX_DEVICES 4 // 4 blocks #define CS_PIN 9 // The Arduino UNO R4 pin connected to the CS pin of LED matrix // create an instance of the MD_Parola class MD_Parola ledMatrix = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES); void setup() { ledMatrix.begin(); // initialize the object ledMatrix.setIntensity(15); // set the brightness of the LED matrix display (from 0 to 15) ledMatrix.displayClear(); // clear led matrix display ledMatrix.displayScroll("Hello, DIYables", PA_CENTER, PA_SCROLL_LEFT, 100); } void loop() { if (ledMatrix.displayAnimate()) { ledMatrix.displayReset(); } }

To see more text effects, go to MD_Parola Library Reference.

Video Tutorial

The below video demo uses the below code:

/* * This Arduino UNO R4 code was developed by newbiely.com * * This Arduino UNO R4 code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/arduino-uno-r4/arduino-uno-r4-led-matrix */ #include <MD_MAX72xx.h> #include <SPI.h> // ============================================================ // MAX7219 // ============================================================ #define HARDWARE_TYPE MD_MAX72XX::FC16_HW #define MAX_DEVICES 4 #define CS_PIN 9 MD_MAX72XX mx = MD_MAX72XX(HARDWARE_TYPE, CS_PIN, MAX_DEVICES); // ============================================================ // BUTTONS // ============================================================ #define BTN_LEFT 2 #define BTN_RIGHT 3 #define BTN_ROTATE 4 #define BUZZER_PIN 6 // ============================================================ // GAME BOARD // ============================================================ #define BOARD_W 8 #define BOARD_H 32 bool board[BOARD_H][BOARD_W]; // ============================================================ // TIMING // ============================================================ unsigned long lastFallTime = 0; const unsigned long NORMAL_FALL = 700; const unsigned long SOFT_DROP_FALL = 80; #define LONG_PRESS_TIME 400 // ============================================================ // SCORE // ============================================================ unsigned long score = 0; unsigned int lines = 0; unsigned int level = 1; unsigned long highScore = 0; // ============================================================ // GAME STATE // ============================================================ bool gameOver = false; bool gameStarted = false; // ============================================================ // TETROMINO DEFINITIONS // ============================================================ struct Point { int x; int y; }; struct Tetromino { Point block[4]; uint8_t color; }; Tetromino pieces[7] = { // I { {{0,0}, {1,0}, {2,0}, {3,0}}, 0 }, // O { {{0,0}, {1,0}, {0,1}, {1,1}}, 1 }, // T { {{1,0}, {0,1}, {1,1}, {2,1}}, 2 }, // L { {{0,0}, {0,1}, {0,2}, {1,2}}, 3 }, // J { {{1,0}, {1,1}, {1,2}, {0,2}}, 4 }, // S { {{1,0}, {2,0}, {0,1}, {1,1}}, 5 }, // Z { {{0,0}, {1,0}, {1,1}, {2,1}}, 6 } }; // ============================================================ // CURRENT PIECE // ============================================================ int currentPiece; int currentX; int currentY; int rotationState = 0; // ============================================================ // BUTTON STATE // ============================================================ bool lastLeftState = HIGH; bool lastRightState = HIGH; bool lastRotateState = HIGH; unsigned long rotatePressTime = 0; bool softDrop = false; bool rotateHandled = false; // ============================================================ // DISPLAY COORDINATE MAPPING // ============================================================ void setPixel(int x, int y, bool state) { if (x < 0 || x >= BOARD_W || y < 0 || y >= BOARD_H) { return; } // Reverse vertical direction int physicalColumn = (BOARD_H - 1) - y; int physicalRow = x; mx.setPoint( physicalRow, physicalColumn, state ); } // ============================================================ // CLEAR DISPLAY // ============================================================ void clearDisplay() { mx.clear(); } // ============================================================ // DRAW BOARD // ============================================================ void drawBoard() { clearDisplay(); // Draw fixed blocks for (int y = 0; y < BOARD_H; y++) { for (int x = 0; x < BOARD_W; x++) { if (board[y][x]) { setPixel( x, y, true ); } } } // Draw falling piece drawCurrentPiece(); } // ============================================================ // GET ROTATED BLOCK POSITION // ============================================================ Point getRotatedPoint( Point p, int rotation ) { Point r; switch (rotation % 4) { case 0: r.x = p.x; r.y = p.y; break; case 1: r.x = -p.y; r.y = p.x; break; case 2: r.x = -p.x; r.y = -p.y; break; case 3: r.x = p.y; r.y = -p.x; break; } return r; } // ============================================================ // CHECK COLLISION // ============================================================ bool checkCollision( int pieceX, int pieceY, int rotation ) { for (int i = 0; i < 4; i++) { Point p = getRotatedPoint( pieces[currentPiece].block[i], rotation ); int x = pieceX + p.x; int y = pieceY + p.y; // Left / Right wall if (x < 0 || x >= BOARD_W) { return true; } // Bottom if (y >= BOARD_H) { return true; } // Existing block if (y >= 0 && board[y][x]) { return true; } } return false; } // ============================================================ // DRAW CURRENT PIECE // ============================================================ void drawCurrentPiece() { for (int i = 0; i < 4; i++) { Point p = getRotatedPoint( pieces[currentPiece].block[i], rotationState ); int x = currentX + p.x; int y = currentY + p.y; if (x >= 0 && x < BOARD_W && y >= 0 && y < BOARD_H) { setPixel( x, y, true ); } } } // ============================================================ // RANDOM PIECE // ============================================================ void spawnPiece() { currentPiece = random(0, 7); rotationState = 0; currentX = 3; currentY = 0; // I piece if (currentPiece == 0) { currentX = 2; } // Game over if piece cannot spawn if (checkCollision( currentX, currentY, rotationState)) { gameOver = true; if (score > highScore) { highScore = score; } soundGameOver(); } } // ============================================================ // LOCK PIECE // ============================================================ void lockPiece() { for (int i = 0; i < 4; i++) { Point p = getRotatedPoint( pieces[currentPiece].block[i], rotationState ); int x = currentX + p.x; int y = currentY + p.y; if (x >= 0 && x < BOARD_W && y >= 0 && y < BOARD_H) { board[y][x] = true; } } soundLock(); // Check completed lines clearLines(); // New piece spawnPiece(); lastFallTime = millis(); } // ============================================================ // CLEAR LINES // ============================================================ void clearLines() { int cleared = 0; for (int y = BOARD_H - 1; y >= 0; y--) { bool full = true; for (int x = 0; x < BOARD_W; x++) { if (!board[y][x]) { full = false; break; } } if (full) { cleared++; // Move rows down for (int yy = y; yy > 0; yy--) { for (int x = 0; x < BOARD_W; x++) { board[yy][x] = board[yy - 1][x]; } } // Clear top row for (int x = 0; x < BOARD_W; x++) { board[0][x] = false; } // Check same row again y++; } } // Scoring if (cleared > 0) { lines += cleared; switch (cleared) { case 1: score += 100 * level; break; case 2: score += 300 * level; break; case 3: score += 500 * level; break; case 4: score += 800 * level; break; } // Increase level every 10 lines level = (lines / 10) + 1; soundLineClear(cleared); } } // ============================================================ // MOVE LEFT // ============================================================ void moveLeft() { if (gameOver) { return; } if (!checkCollision( currentX - 1, currentY, rotationState)) { currentX--; drawBoard(); soundLeft(); } } // ============================================================ // MOVE RIGHT // ============================================================ void moveRight() { if (gameOver) { return; } if (!checkCollision( currentX + 1, currentY, rotationState)) { currentX++; drawBoard(); soundRight(); } } // ============================================================ // ROTATE // ============================================================ void rotatePiece() { if (gameOver) { return; } int newRotation = (rotationState + 1) % 4; // Normal rotation if (!checkCollision( currentX, currentY, newRotation)) { rotationState = newRotation; drawBoard(); return; } // Wall kick LEFT if (!checkCollision( currentX - 1, currentY, newRotation)) { currentX--; rotationState = newRotation; drawBoard(); return; } // Wall kick RIGHT if (!checkCollision( currentX + 1, currentY, newRotation)) { currentX++; rotationState = newRotation; drawBoard(); return; } } // ============================================================ // MOVE DOWN // ============================================================ bool moveDown() { if (gameOver) { return false; } if (!checkCollision( currentX, currentY + 1, rotationState)) { currentY++; return true; } // Cannot move further lockPiece(); return false; } // ============================================================ // SOFT DROP // ============================================================ void handleSoftDrop() { if (gameOver) { return; } static unsigned long lastSoftDrop = 0; if (millis() - lastSoftDrop >= SOFT_DROP_FALL) { lastSoftDrop = millis(); if (moveDown()) { score += 1; } drawBoard(); } } // ============================================================ // NORMAL FALL // ============================================================ void handleNormalFall() { if (gameOver) { return; } unsigned long fallSpeed; fallSpeed = NORMAL_FALL - ((level - 1) * 50); if (fallSpeed < 100) { fallSpeed = 100; } if (millis() - lastFallTime >= fallSpeed) { lastFallTime = millis(); moveDown(); drawBoard(); } } // ============================================================ // BUTTON HANDLING // ============================================================ void handleButtons() { bool leftState = digitalRead(BTN_LEFT); bool rightState = digitalRead(BTN_RIGHT); bool rotateState = digitalRead(BTN_ROTATE); // ========================================================== // LEFT // ========================================================== if (leftState == LOW && lastLeftState == HIGH) { moveLeft(); } // ========================================================== // RIGHT // ========================================================== if (rightState == LOW && lastRightState == HIGH) { moveRight(); } // ========================================================== // ROTATE BUTTON PRESSED // ========================================================== if (rotateState == LOW && lastRotateState == HIGH) { rotatePressTime = millis(); rotateHandled = false; } // ========================================================== // ROTATE BUTTON HELD // ========================================================== if (rotateState == LOW) { if (!rotateHandled && millis() - rotatePressTime >= LONG_PRESS_TIME) { rotateHandled = true; softDrop = true; } } // ========================================================== // ROTATE BUTTON RELEASED // ========================================================== if (rotateState == HIGH && lastRotateState == LOW) { unsigned long pressDuration = millis() - rotatePressTime; // Short press = ROTATE if (pressDuration < LONG_PRESS_TIME && !rotateHandled) { rotatePiece(); soundRotate(); } // Stop soft drop softDrop = false; } lastLeftState = leftState; lastRightState = rightState; lastRotateState = rotateState; } // ============================================================ // START MUSIC // ============================================================ // // Arcade-style startup melody. // // Không dùng tone liên tục. // Mỗi nốt kết thúc trước khi nốt tiếp theo bắt đầu. // ============================================================ void playStartMusic() { clearDisplay(); // ---------------------------------------------------------- // Intro LED sweep // ---------------------------------------------------------- for (int y = 0; y < BOARD_H; y++) { for (int x = 0; x < BOARD_W; x++) { setPixel( x, y, true ); } // Melody notes during sweep if (y == 0) { tone(BUZZER_PIN, 523, 100); } if (y == 4) { tone(BUZZER_PIN, 659, 100); } if (y == 8) { tone(BUZZER_PIN, 784, 100); } if (y == 12) { tone(BUZZER_PIN, 1047, 150); } if (y == 18) { tone(BUZZER_PIN, 784, 100); } if (y == 22) { tone(BUZZER_PIN, 1047, 100); } if (y == 26) { tone(BUZZER_PIN, 1319, 180); } delay(18); } delay(100); clearDisplay(); // ---------------------------------------------------------- // READY melody // ---------------------------------------------------------- tone(BUZZER_PIN, 784, 120); delay(150); tone(BUZZER_PIN, 988, 120); delay(150); tone(BUZZER_PIN, 1175, 120); delay(150); tone(BUZZER_PIN, 1568, 250); delay(300); clearDisplay(); noTone(BUZZER_PIN); } // ============================================================ // LEFT SOUND // ============================================================ void soundLeft() { tone( BUZZER_PIN, 650, 70 ); } // ============================================================ // RIGHT SOUND // ============================================================ void soundRight() { tone( BUZZER_PIN, 850, 70 ); } // ============================================================ // ROTATE SOUND // ============================================================ void soundLock() { tone(BUZZER_PIN, 450, 120); delay(130); tone(BUZZER_PIN, 300, 100); } // ============================================================ // ROTATE SOUND // ============================================================ void soundRotate() { tone( BUZZER_PIN, 1200, 100 ); } // ============================================================ // LINE CLEAR SOUND // ============================================================ void soundLineClear(int count) { if (count == 1) { tone( BUZZER_PIN, 1000, 120 ); delay(150); tone( BUZZER_PIN, 1400, 160 ); } else if (count == 2) { tone( BUZZER_PIN, 1000, 100 ); delay(120); tone( BUZZER_PIN, 1300, 100 ); delay(120); tone( BUZZER_PIN, 1700, 180 ); } else { tone( BUZZER_PIN, 1200, 100 ); delay(120); tone( BUZZER_PIN, 1500, 100 ); delay(120); tone( BUZZER_PIN, 1800, 100 ); delay(120); tone( BUZZER_PIN, 2200, 250 ); } } // ============================================================ // GAME OVER SOUND // ============================================================ void soundGameOver() { tone( BUZZER_PIN, 700, 180 ); delay(210); tone( BUZZER_PIN, 500, 180 ); delay(210); tone( BUZZER_PIN, 350, 220 ); delay(250); tone( BUZZER_PIN, 220, 450 ); delay(500); noTone(BUZZER_PIN); } // ============================================================ // GAME OVER ANIMATION // ============================================================ void gameOverAnimation() { for (int i = 0; i < 4; i++) { drawBoard(); delay(250); clearDisplay(); delay(250); } } // ============================================================ // RESET GAME // ============================================================ void resetGame() { for (int y = 0; y < BOARD_H; y++) { for (int x = 0; x < BOARD_W; x++) { board[y][x] = false; } } score = 0; lines = 0; level = 1; gameOver = false; spawnPiece(); lastFallTime = millis(); drawBoard(); } // ============================================================ // SETUP // ============================================================ void setup() { Serial.begin(115200); // Buttons pinMode( BTN_LEFT, INPUT_PULLUP ); pinMode( BTN_RIGHT, INPUT_PULLUP ); pinMode( BTN_ROTATE, INPUT_PULLUP ); // Buzzer pinMode( BUZZER_PIN, OUTPUT ); // MAX7219 mx.begin(); mx.control( MD_MAX72XX::INTENSITY, 5 ); mx.control( MD_MAX72XX::UPDATE, true ); mx.clear(); // Random seed randomSeed( analogRead(A0) ); // ========================================================== // START MUSIC // ========================================================== playStartMusic(); // ========================================================== // START GAME // ========================================================== resetGame(); gameStarted = true; } // ============================================================ // MAIN LOOP // ============================================================ void loop() { if (!gameStarted) { return; } // ========================================================== // GAME OVER // ========================================================== if (gameOver) { gameOverAnimation(); delay(500); // Restart resetGame(); return; } // ========================================================== // INPUT // ========================================================== handleButtons(); // ========================================================== // SOFT DROP // ========================================================== if (softDrop) { handleSoftDrop(); } else { handleNormalFall(); } }

※ OUR MESSAGES

  • As freelancers, We are AVAILABLE for HIRE. See how to outsource your project to us
  • Please feel free to share the link of this tutorial. However, Please do not use our content on any other websites. We invested a lot of effort and time to create the content, please respect our work!