Arduino UNO R4 WiFi LED Matrix Guide: Graphics, Animation and Memory

Complete Arduino UNO R4 WiFi LED matrix guide: draw 12x8 bitmaps, create animations, scroll text, understand frame encoding and memory use, and build practical status displays and mini games.

The Arduino UNO R4 WiFi includes one of the most useful onboard displays ever fitted to a standard UNO: a 12×8 red LED matrix with 96 individually controllable LEDs.

You can use it to display:

  • icons;
  • numbers;
  • sensor status;
  • simple graphs;
  • animations;
  • scrolling text;
  • progress indicators;
  • small games.

No external display, shift register or wiring is required.

The matrix is controlled by the RA4M1 through Charlieplexing and Arduino provides the Arduino_LED_Matrix library as part of the UNO R4 board package.

This guide covers the complete practical workflow: starting the matrix, drawing bitmaps, creating animations, scrolling text, understanding the 96-bit framebuffer, calculating animation memory use and avoiding the most common mistakes.

UNO R4 WiFi LED Matrix Specifications

Feature UNO R4 WiFi LED Matrix
Matrix size 12 columns × 8 rows
Total LEDs 96
LED colour Red
Colour depth On/off monochrome in normal UNO R4 library use
Drive method Charlieplexing
Main library Arduino_LED_Matrix.h
Main class ArduinoLEDMatrix
Framebuffer size 96 bits = 12 bytes
Standard animation frame storage 16 bytes per frame
Processor driving matrix Renesas RA4M1

Which UNO R4 Has the Matrix?

The built-in 12×8 matrix is a feature of the UNO R4 WiFi.

The UNO R4 Minima uses the same RA4M1 microcontroller but does not include the onboard matrix.

If you are choosing between the two boards, see our UNO R4 WiFi vs UNO R4 Minima comparison.

How the Matrix Is Connected

The 96 LEDs are not connected to 96 separate GPIO pins.

Arduino uses a technique called Charlieplexing.

Only 11 RA4M1 pins are required to control the complete display.

Arduino documents the MCU pins used by the matrix as:

The library rapidly changes pin directions and output levels to illuminate one LED position at a time.

Persistence of vision makes the matrix appear continuously lit.

Do Not Manually Drive the Matrix Pins

The matrix-driving pins are part of the board’s internal design.

Use the official matrix library rather than trying to control the LED network directly with digitalWrite().

The library handles:

  • LED scanning;
  • Charlieplexing direction control;
  • refresh timing;
  • frame switching;
  • animation timing.

Include the Matrix Library

Start with:

Then create a matrix object:

Initialise it in setup():

Your First 12×8 Bitmap

One of the easiest ways to create a static image is to use an 8-row by 12-column byte array.

Render it with:

Complete Bitmap Example

Understanding the Coordinate System

Think of the display as:

So a bitmap is normally written as:

with:

A Simple Arrow Icon

This approach is readable and excellent for icons that you want to edit manually.

Turning the Matrix Off

The library provides:

This loads an all-off frame into the display.

The Actual Framebuffer Is Only 12 Bytes

The matrix contains:

Each pixel only needs one bit:

The current Arduino matrix library therefore maintains a live framebuffer of only 12 bytes.

This is tiny compared with most graphical displays.

For comparison:

  • 12×8 monochrome matrix: 12 bytes;
  • 128×64 monochrome OLED: 1024 bytes;
  • 320×240 16-bit framebuffer: 153,600 bytes.

Why the Bitmap Array Uses 96 Bytes

A readable bitmap like:

uses one byte for every logical pixel.

That means:

The library converts those 96 bytes into the packed 12-byte internal framebuffer.

So there are two different memory figures:

Packed Frames

The library can also work with a packed frame represented by three 32-bit integers:

Three words contain:

which maps exactly to the 96 LED states.

This representation only needs:

per static image.

Loading a Packed Frame

Example:

Packed frames are efficient but much less readable than an 8×12 visual bitmap.

Animation Frame Format

The standard sequence format uses four 32-bit values for every animation frame:

For example:

The first three words contain the 96 LED bits.

The fourth word stores the duration of that frame in milliseconds.

Animation Memory Calculation

Each stored animation frame uses:

So:

Frames Raw sequence storage
1 16 bytes
10 160 bytes
30 480 bytes
60 960 bytes
100 1600 bytes
500 8000 bytes

This is why surprisingly long animations can fit comfortably in UNO R4 program memory.

Flash vs SRAM for Animations

The RA4M1 has:

  • 256 kB Flash;
  • 32 kB SRAM.

Large static animation tables should normally be declared as const:

This allows the compiler to keep the immutable sequence in program storage rather than treating it as a writable RAM object.

The current matrix library does not copy the entire sequence into a second animation buffer when loadSequence() is called. It retains a pointer to the sequence and loads each frame into the small live framebuffer as needed.

This is why an animation containing hundreds of frames does not automatically consume the same amount of SRAM as its source table.

Load and Play an Animation

The standard workflow is:

The Boolean argument to play() controls looping.

For example:

loops continuously.

While:

plays the sequence once.

Complete Animation Example

Why Animation Continues Without delay()

The library uses a hardware timer to refresh the Charlieplexed matrix and manage frame timing.

That means your normal loop() can continue performing other work while an animation is playing.

For example:

while the matrix keeps refreshing in the background.

Do Not Refresh the Entire Matrix Manually in loop()

You do not need code such as:

The library already handles the multiplexing.

Your application should think in terms of frames, not individual scan cycles.

Manual Frame Stepping

The library also provides:

This advances to the next frame in the loaded sequence.

Manual stepping is useful when:

  • a button controls the animation;
  • a sensor determines the next frame;
  • you want deterministic application-driven timing;
  • you are building a game.

Render a Specific Frame Number

The current library also includes:

This selects a specific frame from the loaded sequence.

For example:

This is useful for:

  • menus;
  • status icons;
  • game states;
  • sensor-level indicators.

Detect When a Sequence Finishes

The library provides:

which can be used to detect completion of a non-looping sequence.

Example:

Animation Completion Callback

You can also register a callback:

However, the current library explicitly notes that the callback is fired from the interrupt context.

Keep the callback extremely short.

A good pattern is:

Then perform normal work later in loop().

Do Not Use Heavy Code in the Matrix Callback

Avoid doing this inside a callback:

  • long Serial output;
  • Wi-Fi requests;
  • file access;
  • long calculations;
  • delay().

The matrix callback executes from the timer/interrupt environment and should return quickly.

Autoscroll

The matrix library supports automatic frame advancement with:

For example:

The official source notes that automatic scrolling can be less precise than calling next() at carefully controlled times.

For simple visual animations this normally does not matter.

Scrolling Text

The matrix library can also work with ArduinoGraphics support to draw and scroll text.

A typical pattern is:

The exact text workflow depends on the ArduinoGraphics-enabled build supplied with the UNO R4 board package.

The important concept is that the 12-pixel width is too narrow for most words, so text is normally scrolled across the display.

Why 5×7 Fonts Work Well

The display is eight pixels high.

A 5×7 font fits naturally because:

  • seven pixels are available for character height;
  • one row remains for vertical spacing or positioning;
  • characters remain readable.

Only around two characters fit across 12 columns at once, so horizontal scrolling is generally required for longer strings.

Status Display Example

The matrix is excellent for status icons.

Instead of connecting an LCD, a device could use:

For a headless IoT device, that can be enough local feedback for most situations.

Wi-Fi Status Indicator

UNO R4 WiFi is particularly well suited to a simple connection-status animation.

For example:

The RA4M1 can update the matrix while the onboard ESP32-S3 handles Wi-Fi.

Sensor Bar Graph

A 12-column display can become a compact bar graph.

Suppose a sensor produces:

Map it into:

and illuminate entire columns to represent the level.

This works well for:

  • battery state;
  • light level;
  • temperature range;
  • Wi-Fi RSSI;
  • tank level;
  • motor load.

Simple Dynamic Bar Graph

This uses 96 bytes of writable RAM for the editable bitmap.

Moving Pixel Example

A very small game or animation can be made by moving one active pixel through the bitmap.

This is enough to build:

  • Snake-style games;
  • Pong;
  • maze games;
  • reaction games;
  • simple position displays.

Game State Fits Easily in RAM

The matrix itself is tiny.

For example, a game might use:

This is trivial compared with the UNO R4’s 32 kB SRAM.

The limitation is display resolution, not memory.

Animation Generator

Arduino provides an online graphical tool for designing matrix images and animations.

This is much easier than calculating packed hexadecimal frames by hand.

A typical workflow is:

  1. draw frames visually;
  2. set animation timing;
  3. export the generated array;
  4. paste it into the sketch;
  5. load it with matrix.loadSequence();
  6. play it with matrix.play().

For a large animation this is strongly preferable to manually editing 96-bit hexadecimal values.

Use Separate Header Files for Large Animations

Large generated arrays can make the main sketch difficult to read.

Put them in a separate file:

Then:

This is the same structure used by Arduino’s own introductory matrix example.

Memory Example: 120-Frame Animation

A 120-frame animation uses approximately:

That is less than 2 kB of raw sequence data.

Even several hundred frames can be practical if kept as constant program data.

The duration of an animation therefore does not need to be tiny just because the RA4M1 has 32 kB SRAM.

Memory Example: One Minute of Animation

Suppose you store ten frames per second:

Raw sequence storage:

That is still modest relative to 256 kB of Flash.

Of course, code, libraries and other constant data also consume Flash, so a real project should still check compiler memory reports.

Slow Animations Need Fewer Frames

Because every frame stores its own duration, you do not need hundreds of duplicate frames just to hold an image on screen.

Instead of:

store one frame with a longer duration.

This can dramatically reduce animation size.

Frame Duration Is a 32-Bit Value

In the current sequence structure, the fourth uint32_t stores the interval.

This gives much more timing range than an 8-bit duration field would provide.

For practical LED animations, durations are usually in the tens or hundreds of milliseconds.

Brightness Control

The built-in matrix is primarily exposed as an on/off LED matrix rather than a conventional multi-level grayscale display.

Arduino’s normal UNO R4 matrix examples focus on monochrome graphics and animation.

If you need:

  • precise per-pixel brightness;
  • RGB colour;
  • high grayscale depth;

use an external LED matrix or display designed for that purpose.

Can You PWM the Matrix Yourself?

It is theoretically possible to implement more complex duty-cycle control, but doing so would mean working against the library’s Charlieplexing and timer refresh system.

For most applications, treat each pixel as:

and use animation patterns to create visual effects.

Timer Use

The current matrix library obtains an available RA4M1 timer when matrix.begin() is called.

It uses that timer to refresh the display at high speed.

This means advanced projects using many hardware timers should remember that the matrix itself consumes a timer resource while active.

Most normal Arduino applications will never notice this.

What Happens If matrix.begin() Fails?

The current library’s begin() returns an integer/Boolean-like result.

You can check it:

Failure could occur if an appropriate timer resource cannot be obtained.

Matrix and Normal Arduino Pins

The matrix is wired to internal RA4M1 pins rather than simply taking over all of the UNO shield pins.

You can still use the normal Arduino headers for:

  • digital I/O;
  • analog input;
  • SPI;
  • I2C;
  • UART;
  • CAN;

while the matrix is active.

This is one reason the onboard display is so useful: it adds visual output without consuming the normal shield connector positions.

Matrix and Wi-Fi

The LED matrix is driven by the RA4M1.

Wi-Fi is handled through the ESP32-S3 companion.

So a sketch can simultaneously:

  • maintain an MQTT connection;
  • read sensors;
  • control outputs;
  • animate the matrix.

For example, the matrix can indicate:

Matrix and the Main LED

The matrix does not replace LED_BUILTIN.

You can use both at the same time.

Arduino’s own introductory example plays a heart animation on the matrix while blinking the normal built-in LED from the main loop.

Common Problem: Nothing Appears

Check:

  1. #include "Arduino_LED_Matrix.h" is present.
  2. An ArduinoLEDMatrix object was created.
  3. matrix.begin() was called.
  4. The bitmap dimensions are 8×12.
  5. The frame contains actual set bits.
  6. You selected UNO R4 WiFi rather than UNO R4 Minima in the IDE.
  7. The Arduino UNO R4 board package is current.

Common Problem: Bitmap Is Rotated or Mirrored

This usually happens because the source array was designed with a different assumed row/column orientation.

Start with a simple test pattern such as:

then confirm the coordinate system before generating complex artwork.

Common Problem: Animation Only Shows One Frame

Check that you used:

rather than loading a single static frame.

Also confirm that each frame has a non-zero duration value.

Common Problem: Animation Runs Once

Use:

for continuous looping.

With:

the sequence plays once.

Common Problem: Code Uses the Wrong Class Name

The header file is:

but the class used by current UNO R4 examples is:

So:

is the correct pattern.

Common Problem: Huge RAM Usage

If you create a large animation array without const:

the compiler may need writable storage for it.

For fixed generated animations, declare the sequence constant:

This is particularly important when animations become large.

Common Problem: Heavy Work in Callback

Remember that the sequence callback is executed from interrupt context.

Do this:

not this:

Practical UI Patterns

The matrix is small, so interfaces should be designed around symbols rather than detailed graphics.

Good patterns include:

  • single-letter states;
  • arrows;
  • battery bars;
  • Wi-Fi symbols;
  • check marks;
  • crosses;
  • progress bars;
  • animated dots;
  • simple numeric values.

Displaying Numbers

Eight rows are enough for a readable small numeric font.

The display can show:

  • temperature;
  • percentage;
  • speed;
  • error codes;
  • countdown values.

For multi-digit values, either scroll the number or switch between digits.

Progress Indicator

An upload progress indicator can map:

onto:

or more simply:

Column-based progress is easier to read from a distance.

Matrix as a Debug Tool

The onboard display is surprisingly useful when Serial is unavailable.

For example:

This can make a headless installation much easier to troubleshoot.

Matrix vs External OLED

Use the built-in matrix when you need:

  • simple status;
  • icons;
  • animations;
  • basic numbers;
  • zero extra wiring.

Use an OLED or TFT when you need:

  • long text;
  • menus;
  • graphs with labels;
  • multiple measurements simultaneously;
  • high-resolution graphics.

The matrix is not meant to replace every display. Its strength is that it is already there.

UNO R4 WiFi Matrix vs External Modulino Matrix

Arduino also offers an 8×12 Modulino LED Matrix with the same basic 96-LED concept and code compatibility.

The external module is useful when:

  • you need a second matrix;
  • you use a board without the onboard display;
  • the matrix must be physically separated from the controller.

The built-in UNO R4 WiFi matrix remains the simplest option for local board status.

UNO R4 WiFi Matrix vs UNO R3

UNO R3 has no built-in graphical display.

A comparable R3 project normally needs:

  • an LED matrix module;
  • driver hardware;
  • wiring;
  • additional library code.

The R4 WiFi integrates all of this into the board.

For the larger generational comparison, see our Arduino UNO R4 vs UNO R3 guide.

Memory Summary

Object Approximate storage
Live packed framebuffer 12 bytes
Readable uint8_t[8][12] bitmap 96 bytes
Packed static frame uint32_t[3] 12 bytes
Animation frame uint32_t[4] 16 bytes
100-frame animation table 1600 bytes
500-frame animation table 8000 bytes

Best Practices

  1. Use renderBitmap() for simple editable icons.
  2. Use packed frame sequences for longer animations.
  3. Declare fixed animation tables const.
  4. Keep animation callbacks very short.
  5. Use frame duration rather than duplicating identical frames.
  6. Use the online animation editor for complex sequences.
  7. Keep the UI symbolic; 12×8 is small.
  8. Remember the matrix is monochrome, not RGB.
  9. Let the library handle Charlieplexing and refresh timing.

Final Thoughts

The UNO R4 WiFi’s 12×8 LED matrix is far more useful than a decorative feature.

With 96 individually controlled LEDs, it can serve as:

  • a status display;
  • a small dashboard;
  • a progress indicator;
  • an animation surface;
  • a debugging tool;
  • a simple gaming display.

The display is also extremely memory-efficient.

The actual framebuffer is only:

and the standard animation structure uses only:

because three 32-bit words store the LED image and one additional word stores the frame duration.

This means even fairly long animations fit comfortably alongside normal UNO R4 code, especially when fixed sequences are declared as constant program data.

The easiest development path is:

For many projects, the matrix eliminates the need for an external display entirely. It is small, but because it is built into the board and driven in the background, it is almost free to use.

Share your love