# Adafruit Protomatter RGB Matrix Library

## Overview

https://www.youtube.com/watch?v=x8MAx_PMnN4

 **Adafruit\_Protomatter** is a C library (with Arduino and [CircuitPython](https://learn.adafruit.com/rgb-led-matrices-matrix-panels-with-circuitpython) front-ends) for driving RGB LED matrices (colloquially called “HUB75” matrices but that’s a vague term and not entirely accurate).&nbsp;We already have one such library — [RGBmatrixPanel](https://learn.adafruit.com/32x16-32x32-rgb-led-matrix/library) for Arduino — it’s older code that works fine for AVR chips (and a couple others) but has some limitations.

This guide was mostly to assist with **porting** &nbsp;Adafruit\_Protomatter to new hardware, but has since been expanded with installation and basic use.

“Protomatter” was intended as a stand-in name until something better was decided upon, but we’d already moved in by that point.

**In CircuitPython, it’s only seen by the sensible name** _ **rgbmatrix.** _ But if working with the underlying C code or the Arduino library…sorry, you’re stuck with the awkward-to-type Protomatter name.

It originates in a quote from&nbsp;the character David Marcus in _Star Trek III:_ “I used protomatter in the Genesis matrix.” Hopefully the library doesn’t develop a similar reputation as “unstable” and “dangerously unpredictable.”

![led_matrices_trek3.jpg](https://cdn-learn.adafruit.com/assets/assets/000/091/089/medium640/led_matrices_trek3.jpg?1589248388)

Protomatter was planned with newer hardware in mind and will _not_ be back-ported to AVR — simply use the prior RGBmatrixPanel there if you need it.&nbsp;The new library takes a casual approach to some things and isn’t particularly RAM-efficient, a crucial concern on AVRs.&nbsp;What Protomatter _does_ provide includes:

- More flexibility in matrix chain width and height (RGBmatrixPanel only supports a few sizes).
- More flexibility in pin selection (old code was specifically designed for an Arduino UNO shield).
- Configurable bit depth, up to the maximum “565” color format used by Adafruit\_GFX.
- Mostly doesn’t rely on esoteric peripherals — sticks with basic “PORT” GPIO and a timer interrupt. _Some exceptions were made for ESP32-S3 and -S3._
- Mostly avoids cycle-counting.

A likely candidate device for porting will have:

- A reasonably fast 32-bit RISC core or similar (e.g. ARM, ESP32). Minimum we’ve used is a 48 MHz Cortex-M0+.
- One or more timer peripherals, 16-bit or better, with configurable period and with interrupts.
- GPIO with atomic bit-set and bit-clear registers, typically 32 bits wide…ideally tolerating writes to individual sub-bytes or words.
- RAM usage depends on matrix size, bit depth and GPIO pin selection. Minimum device we’ve used has 32 KB RAM (total for device, _not_ all consumed by the library).

As currently written, Protomatter eschews the use of DMA or special peripherals beyond what’s described above. Goal is simply to get this working on a variety of devices with a minimum of fuss. We can tweak and optimize later.

## **Things You’ll Need**

- A datasheet or reference manual for the device being ported to.
- Hardware to test on. Having both a logic analyzer _and_ a known-compatible-with-existing-devices RGB matrix is really helpful to verify that all signals are doing the right things at the right times.

# Adafruit Protomatter RGB Matrix Library

## Installing the Protomatter Library

To work with the Protomatter library in the Arduino IDE, access the Library Manager from the _Sketch_ menu…

**Sketch→Include Library→Manage Libraries…**

![](https://cdn-learn.adafruit.com/assets/assets/000/096/161/medium800/led_matrices_lib-manager.png?1603224932)

Type “protomatter” in the search field at the top-right of the Library Manager window, then click _Install_ for the **Adafruit Protomatter** library.

![](https://cdn-learn.adafruit.com/assets/assets/000/096/129/medium800/led_matrices_install-protomatter.png?1603168990)

The very latest versions of the Arduino IDE automatically install other dependent libraries. If you’re on a slightly earlier version, search for “gfx” and install **Adafruit GFX Library** manually…

![](https://cdn-learn.adafruit.com/assets/assets/000/096/130/medium800/led_matrices_install-gfx.png?1603169001)

## **_Adafruit\_GFX_ is the same library that drives many of our LCD and OLED displays…if you’ve done other graphics projects, you might already be familiar! And if not, [we have a separate guide explaining all of the available drawing functions](https://learn.adafruit.com/adafruit-gfx-graphics-library/overview).&nbsp;Most folks can get a quick start by looking at the Protomatter library’s “simple” and “doublebuffer\_scrolltext” examples and tweaking these for their needs.**
Some of the Protomatter examples rely on the **Adafruit\_PixelDust** , **Adafruit\_LIS3DH** and/or **AnimatedGIF** libraries…so again, search for and install those if not already handled automatically by Arduino.

If you’re using an **Adafruit MatrixPortal** board specifically, [the MatrixPortal guide has a whole page for all of the needed libraries](https://learn.adafruit.com/adafruit-matrixportal-m4/arduino-libraries).

# For Developers

If your plan is to _adapt Protomatter to new devices,_ not just use it with existing boards, you’ll want to remove any Arduino-installed version of the library and **download it from Github instead**. This way you can submit pull requests for inclusion in future releases of the code.

You’ll find the Protomatter library at&nbsp;[https://github.com/adafruit/Adafruit\_Protomatter](https://github.com/adafruit/Adafruit_Protomatter)

and the GFX library, if any changes are needed, is at [https://github.com/adafruit/Adafruit-GFX-Library](https://github.com/adafruit/Adafruit-GFX-Library)

# Adafruit Protomatter RGB Matrix Library

## Using the Protomatter Library

Let’s look at a **minimal Arduino example** &nbsp;for the Adafruit\_Protomatter library to illustrate how this works (this is pared down from the “ **simple** ” example sketch):

```cpp
#include <Adafruit_Protomatter.h>

uint8_t rgbPins[]  = {7, 8, 9, 10, 11, 12};
uint8_t addrPins[] = {17, 18, 19, 20, 21};
uint8_t clockPin   = 14;
uint8_t latchPin   = 15;
uint8_t oePin      = 16;

Adafruit_Protomatter matrix(
  64, 4, 1, rgbPins, 4, addrPins, clockPin, latchPin, oePin, false);

void setup(void) {
  Serial.begin(9600);

  // Initialize matrix...
  ProtomatterStatus status = matrix.begin();
  Serial.print("Protomatter begin() status: ");
  Serial.println((int)status);
  if(status != PROTOMATTER_OK) {
    for(;;);
  }

  // Make four color bars (red, green, blue, white) with brightness ramp:
  for(int x=0; x<matrix.width(); x++) {
    uint8_t level = x * 256 / matrix.width(); // 0-255 brightness
    matrix.drawPixel(x, matrix.height() - 4, matrix.color565(level, 0, 0));
    matrix.drawPixel(x, matrix.height() - 3, matrix.color565(0, level, 0));
    matrix.drawPixel(x, matrix.height() - 2, matrix.color565(0, 0, level));
    matrix.drawPixel(x, matrix.height() - 1, matrix.color565(level, level, level));
  }

  // Simple shapes and text, showing GFX library calls:
  matrix.drawCircle(12, 10, 9, matrix.color565(255, 0, 0));               // Red
  matrix.drawRect(14, 6, 17, 17, matrix.color565(0, 255, 0));             // Green
  matrix.drawTriangle(32, 9, 41, 27, 23, 27, matrix.color565(0, 0, 255)); // Blue
  matrix.println("ADAFRUIT"); // Default text color is white

  // AFTER DRAWING, A show() CALL IS REQUIRED TO UPDATE THE MATRIX!

  matrix.show(); // Copy data to matrix buffers
}

void loop(void) {
  Serial.print("Refresh FPS = ~");
  Serial.println(matrix.getFrameCount());
  delay(1000);
}
```

Breaking it down into steps…

# Include Protomatter Library

First is to `#include` the library’s header file. This in turn `#includes` **Adafruit\_GFX.h** , so you don’t have to.

```cpp
#include <Adafruit_Protomatter.h>
```

# Setting Up Matrix Pin Usage

The next few lines spell out the pin numbers being used. Using variables for this isn’t entirely necessary…one _could_ just pass the same numeric values directly to functions…but it makes the code a little more self-documenting (and easier to adapt the same sketch for multiple boards — the full example code has #ifdefs for each board with different pin assignments). These also could be `#defines` or `const` if one wants to be all Proper™ about it.

_Technical stuff for developers, skip this if you just want to use the library:_

This is the one part of the Arduino code where **some knowledge of the underlying hardware is required**. `rgbPins[]` and `clockPin` **must all be on the same GPIO PORT peripheral** (e.g. all PORTA, all PORTB, etc.). The **other pins have no such restrictions**. Additionally, if the PORT has an atomic bit-toggle register, RAM requirements are minimized if `rgbPins[]` and `clockPin` are all **within the same byte** of that PORT\*. They _do not need to be contiguous nor in any particular sequence_ within that byte. If not within the same byte, next most efficient has them in the same upper or lower 16-bit word of the PORT. Scattered around a full 32-bit PORT still works but is the least RAM-efficient option.

\* For devices lacking an atomic bit-toggle register…`clockPin` _does not_ need to be in the same byte, but still must be in the same PORT. Should still aim for `rgbPins[]` in a single byte or word though!

With those constraints in mind, here’s what the code looks like for an Adafruit MatrixPortal M4 with a 64x32 pixel matrix:

```cpp
uint8_t rgbPins[]  = {7, 8, 9, 10, 11, 12};
uint8_t addrPins[] = {17, 18, 19, 20, 21};
uint8_t clockPin   = 14;
uint8_t latchPin   = 15;
uint8_t oePin      = 16;
```

The full “simple” example sketch has setups for a number of different boards and adapters.

# Create the Protomatter Object

Next, still in the global area above `setup()`, we call the _constructor._ The Arduino library can only drive one matrix at a time (or one _chain_ of matrices, where “out” from one is linked to “in” of the next), so we just have one instance of an `Adafruit_Protomatter` object here, which we’ll call `matrix`:

```cpp
Adafruit_Protomatter matrix(
  64, 4, 1, rgbPins, 4, addrPins, clockPin, latchPin, oePin, false);
```

The `Adafruit_Protomatter` constructor expects between 9 and 11 arguments depending on the situation. The vital ones here, in order, are:

- `64` — the total matrix chain width, in pixels. This will usually be `64` or `32`, the width of most common RGB LED matrices…but, if you have some other size or multiple matrices chained together, add up the total width here. For example, three chained 32-pixel-wide matrices would be `96`.
- `4` — the bit depth, in planes, from 1 to 6 (see below). More bitplanes provides greater color fidelity at the expense of more RAM. A value of 4 here (4 bits) provides 16 brightness levels each for red, green and blue — yielding 4,096 distinct colors possible.
- `1` — the number of matrix chains in parallel. This will almost always be 1, but the library could conceivably support up to 5, _if_ the hardware driving it is set up _precisely just so._
- `rgbPins` — a `uint8_t` array of pin numbers, which issue the red, green and blue data for the upper and lower half of the matrix (sometimes labeled R1, G1, B1, R2, G2, B2 on the matrix input).. The array should contain six times the prior argument…so, usually, six. If driving two chains in parallel, then 12 pin numbers and so forth. Obviously 12 pins won’t fit in a single PORT byte, and you should aim for the upper or lower 16 bit word in that case, for best RAM utilization. Three or more chains, doesn’t matter, but the pins all do still need to be in the same PORT.
- `4` — the number of row-select “address lines” used by the LED matrix (sometimes labeled A, B, C, etc. on the matrix input). 16-pixel-tall matrices will be three row-select lines, 32-pixel will have four, and 64-pixel will have five. Matrix height is always _inferred_ from this value, not passed explicitly like width.
- `addrPins` — a `uint8_t` array of pin numbers, one for each row-select address line, starting from least-significant bit. These _do not_ need to be on the same PORT as `rgbPins` or each other…they can be mixed about anywhere.
- `clockPin` — pin number which drives the RGB clock (CLK on matrix input). This _must_ be on the same PORT register as `rgbPins`, and in most cases should also try to be in the same _byte._
- `latchPin` — pin number for “latch” signal (LAT on matrix input), indicating end-of-data. Can be any output-capable pin, no special constraints.
- `oePin` — pin number for “!OE” signal (output-enable low, OE on matrix input). Can be any output-capable pin, no special constraints.
- `false` — this flag indicates if the display should be _double-buffered,_ better for animation at the expense of double the RAM usage. Since the protomatter example isn’t using animation, it passes `false` here…but if you look at the **doublebuffer\_scrolltext** example, it uses `true`. A double-buffered display only modifies the matrix between refreshes, avoiding “tearing” artifacts. Optional. Default, if left unspecified, is `false`.
- Not used here, an optional 11th argument supports “tiling” of matrices vertically. Horizontal tiling is already implicit in the first argument — if you had two 64x32 matrices side-by-side, you’d pass 128 there. But if you had _four_ such matrices arranged 2x2, you’d still pass 128 for the first argument, but then add either 2 here (if cabling is in a “progressive” order) or -2 (if a “serpentine” order, where the second row of panels is rotated 180° relative to the first…the cabling is a little easier). The “tiled.ino” example demonstrates this. The concept is explained further in the [CircuitPython LED Matrix guide](https://learn.adafruit.com/rgb-led-matrices-matrix-panels-with-circuitpython/advanced-multiple-panels)…the same principles apply to the Arduino library, the arguments are just a little different here. Default if unspecified is 1 (no vertical tiling).
- Also not used here, an optional 12th argument is a pointer to a hardware-specific timer structure…this is _super exceedingly esoteric_ and not really used for now, but in principle would allow the library to work with other timer peripherals than the default.

# Begin Protomatter Driver

Now, with the matrix object created, inside&nbsp;`setup()`&nbsp;we call its `begin()` function. It’s pretty important to look at the value returned, which is a `ProtomatterStatus` type:

```cpp
ProtomatterStatus status = matrix.begin();
```

Possible return status values include:

- `PROTOMATTER_OK` — everything is good and the program can proceed (otherwise it should _stop…_the example code is not a good neighbor in this regard).
- `PROTOMATTER_ERR_PINS` — the RGB data and clock pins are not all on the same PORT. Can’t continue, the library _requires_ these pins in this layout.
- `PROTOMATTER_ERR_MALLOC` — couldn’t allocate enough memory for display. Can’t continue. This is usually an error that happens in the `begin()` function, but in extreme cases even the constructor could hit an allocation problem, but you won’t get this response until calling `begin()`.
- `PROTOMATTER_ERR_ARG` — some other bad input to function, distinct from PROTOMATTER\_ERR\_PINS. _Exceedingly_ rare, might only happen if constructor failed.

# Draw Shapes & Text Using Adafruit GFX

Then we draw some stuff on the display. **Any graphics primitive supported by the [Adafruit\_GFX library](https://learn.adafruit.com/adafruit-gfx-graphics-library)&nbsp;is available here.**

## **_Adafruit\_GFX_ is the same library that drives many of our LCD and OLED displays…if you’ve done other graphics projects, you might already be familiar! And if not, [we have a separate guide explaining all of the available drawing functions](https://learn.adafruit.com/adafruit-gfx-graphics-library/overview).&nbsp;Most folks can get a quick start by looking at the “simple” and “doublebuffer\_scrolltext” examples and tweaking these for their needs.**

Any color argument passed to a drawing function here is a 16-bit value, with the highest 5 bits representing red brightness (0 to 31), middle 6 bits for green (0 to 63), and least 5 bits for blue (0 to 31). It’s just how Adafruit\_GFX works and is a carryover from early PC graphics and most small LCD/OLED displays.

The effect of bit depth on image quality.&nbsp;Color values are always specified as full 16-bit “565” values, but will quantize to coarser representations at lower bit depths.

Sometimes you might want to avoid 6-bit depth even if RAM permits it. Only green handles the full 6 bits, while red and blue are quantized to 5 bits. This can result in some colors or gradients having slight green or magenta tints to them. 5-bit depth is slightly blockier but colors are more predictable.

![led_matrices_bit-depths.png](https://cdn-learn.adafruit.com/assets/assets/000/091/116/medium640/led_matrices_bit-depths.png?1589342795)

```cpp
matrix.drawCircle(12, 10, 9, matrix.color565(255, 0, 0));               // Red
matrix.drawRect(14, 6, 17, 17, matrix.color565(0, 255, 0));             // Green
...etc...
matrix.show(); // Copy data to matrix buffers
```

Notice though the call to `matrix.show()`&nbsp;at the end. Drawing operations have no immediate effect on the LED matrix, and instead are working on a buffer in RAM behind the scenes. Calling `show()`&nbsp;is **required** — it “pushes” the display data from that buffer to the matrix. You can call it after each drawing function, or group up a bunch of drawing commands with a single `show()` afterward to all appear at once. If you’ve worked with NeoPixel programming, it’s a similar phenomenon.

Since this program isn’t animating anything, it’s finished at that point and `loop()` _could_ be empty.

# Check Refresh Rate

For the sake of curious information though, the example shows the matrix refresh rate using&nbsp;`getFrameCount()`.&nbsp;This returns the _number of frames_ since the _last call to the same function,_ not the refresh rate…but if spaced about one second apart (`delay(1000)`), you get a fair approximation of refresh rate:

```cpp
Serial.println(matrix.getFrameCount());
delay(1000);
```

The matrix refresh rate is influenced by so many factors…processor speed, matrix chain length, bit depth…that it’s difficult to accurately predict ahead of time, so this is a way to see what you get when changing different values in the constructor.

This is a subjective thing, but in broad terms 200 Hz or better should provide a solid image…any less and it starts to become flickery, so you might want a lower bit depth in that case.&nbsp;Conversely, refreshing too fast would waste CPU cycles that you probably want for other tasks like animation. The library does its best to throttle back and not refresh faster than practically needed.

# Adafruit Protomatter RGB Matrix Library

## Adding a New Device

The [**Adafruit\_Protomatter repository on Github**](https://github.com/adafruit/Adafruit_Protomatter) contains all source code for the project. Create a new branch, to facilitate merging pull requests later.

Files there include:

The remaining files in the repository are for Github automation, the Arduino Library manager, Arduino examples and so forth. Probably won’t need editing, except for an occasional bump to the version number in **library.properties**.

## Expanding **arch.h** For a New Part

Inside **arch.h** , you’ll find whole sections of code conditionally compiled in `#if defined` (and corresponding `#endif`) statements. You’ll want to add a new conditional check for your device —&nbsp;neither too specific (there’s usually some #define that broadly relates to a family of related devices), nor too vague that it accidentally compiles on an incompatible chip.

For example, anything in the Microchip SAMD51 family is covered by this:

```cpp
#if defined(__SAMD51__)
  ...a whole bunch of code...
#endif // end __SAMD51__
```

Within each device family section, it’s further divided by (usually) a pair of `#if defined` checks, one to test if we’re compiling in the Arduino environment, another if CircuitPython…perhaps others in the future:

```cpp
#if defined(ARDUINO)
  ...a whole bunch of code...
#elif defined(CIRCUITPY)
  ...a whole bunch of code...
#endif
```

This is because each environment has different convenience functions for certain operations. Arduino, for example, provides&nbsp;`digitalWrite()` for GPIO output. It’s different in CircuitPython, usually distinct to each architecture. The Protomatter code builds on these common operations. Additionally, in the Arduino setting, the library is usually attached to a specific timer/counter peripheral, set at compile-time, whereas in CircuitPython timers are a dynamically-allocated resource…no telling what timer you’re using until run-time.

THEREFORE, each architecture and environment is expected to establish a **known and fixed set of macros or functions** providing these operations. **core.c** , which #includes **arch.h** , then goes about its business using only those known function names, never having to refer to device-specific hardware.

There are **three groups** of macros and functions: one related to **GPIO** , one related to **timers** , and one **miscellaneous** category. With just a few exceptions where noted, the following macros or functions are **required**.

The macros/functions are all prefixed with **\_PM\_** (for Protomatter), sort of a brute-force namespacing of things (to reduce likelihood of collisions with user code) since we’re in simple C here.

## GPIO-Related Macros/Functions

“Pin numbers,” as described here, refer to an environment’s particular indexing of pins, which might not map directly to a device’s PORTs and bits. Arduino digital and analog pins, for example, might really be scattered all over the place, but are exposed to the programmer as a tidy sequential list starting from zero. Other environments may have their own numbering system…but it’s always assumed there’s _some_ numbering system. If not, you’ll need to make one up.

## Timer-Related Macros/Functions

The (void\*) argument passed to these functions is some implementation-specific representation of a timer peripheral. In some cases (such as on SAMD microcontrollers) it’s simply a pointer directly to a timer/counter peripheral’s register base address.&nbsp;If&nbsp;an implementation requires more data associated alongside a peripheral, this&nbsp;could instead be a pointer to a struct, or an integer index.

A **timer interrupt service routine** is also **required** , syntax for which varies&nbsp;between architectures.

Usually the ISR needs to be related to a timer peripheral at compile-time, which is another reason why the Arduino implementation is always tied to a specific timer…other libraries, and the Arduino core itself, have their own ISRs for specific timers, we can’t take them all and dole them out on request.

## Miscellaneous Macros/Functions
 **If adapting to some environment that’s neither Arduino nor CircuitPython:** &nbsp;it’s easiest if the internal representation of an image in RAM matches what Adafruit\_GFX is using: one 16-bit unsigned word per pixel, row major, no row padding. For example, a 64x32 pixel matrix will use 64x32 `uint16_t`s, or 4 kilobytes. The first word corresponds to pixel (0,0) at the top left.

Note that this is the _drawing canvas_ into which points or lines or other primitives are drawn, but it’s distinct from additional space required by Protomatter, which must refresh the matrix plane-by-plane.&nbsp;Call the&nbsp;`_PM_convert_565()` function to process the simple canvas to the shuffled matrix representation and update the display.

If a different canvas representation is used, you’ll have to provide your own conversion function…`_PM_convert_565()` (and the functions it calls in turn) might offer some insights there.

Arduino and CircuitPython implementations already handle this.

## Insights and Surprises

Troubleshooting by just looking at an attached matrix probably won’t yield much success. An oscilloscope or logic analyzer is really helpful. Initially, take a good look at the clock signal…this is the fastest signal the software has to generate (but mustn’t run too fast for the matrix, hence \_PM\_clockHoldHigh and \_PM\_clockHoldLow, if needed). Second, watch the !OE (output enable) signal…if the timer is properly configured and working, you should see the time interval between pulses double with each bitplane (e.g. N microseconds, N\*2 microseconds, N\*4, etc.). You should also see an obvious sequential bit-count among the row-select address lines.

A couple devices threw us for a loop…these problems were surmountable, but worth specifically mentioning here as it may be relevant to future porting efforts…

### STM32

PORT bit-set and bit-clear registers _do not_ correspond to 32-bit ports. Instead, a single 32-bit register has 16-bit set and clear _sections._ At least the bits are contiguous, and \_PM\_portSetRegister() and&nbsp;\_PM\_portClearRegister() can just return pointers to the upper or lower half of the register. Constrained to 16 bits, this does mean that STM32 is limited to a maximum of two concurrent matrix chains.

### ESP32

A little peculiar in that the bit-set and bit-clear registers aren’t entirely atomic. If two set or clear operations occur in rapid sequence (as happens in a couple places in the library), the second has no effect. One solution would be adding NOP instructions, but this is kludgey in that it doesn’t automatically handle faster CPU variants if those come along in the future. Workaround was to always alternate bit-set with bit-clear, using a bitmask of 0 for the second operation. This waits for the first operation to “latch,” and the second has no effect…we can follow up with another bit-set and it works reliably now.

ESP32 needs the timer ISR function (and any sub-functions it calls) in RAM rather than flash. This is done with an&nbsp;`IRAM_ATTR` attribute on a function, and broke our rule of “keep any device-specific code out of **core.c**.” So…if any other devices also require in-RAM ISRs, and if they use an attribute _other_ than `IRAM_ATTR`…one should `#define IRAM_ATTR` to whatever attribute is required there, so that section of the code will handle either case.

### ESP32-S2 and -S3

GPIO set/clear operations are somewhat slower than the original ESP32, which would result in a flickery display, so these two make an exception to the GPIO+timer rule and rely on chip-specific peripherals. On the ESP32-S2, the Dedicated GPIO peripheral is used. On ESP32-S3, the LCD controller peripheral. An interesting side effect of this, because the ESP32 family has very flexible pin-MUXing capabilities, is that _any_ pins can be used to drive the matrix…there’s no specific order or continuity required.


## Related Guides

- [Adafruit RGB Matrix FeatherWings](https://learn.adafruit.com/rgb-matrix-featherwing.md)
- [Adafruit MatrixPortal M4](https://learn.adafruit.com/adafruit-matrixportal-m4.md)
- [Tombstone Matrix Portal](https://learn.adafruit.com/tombstone-matrix-portal.md)
- [Creating MatrixPortal Projects with CircuitPython](https://learn.adafruit.com/creating-projects-with-the-circuitpython-matrixportal-library.md)
- [Motion Controlled Matrix Bed Clock](https://learn.adafruit.com/motion-controlled-matrix-bed-clock.md)
- [RGB Matrix Dreidel Game](https://learn.adafruit.com/rgb-matrix-dreidel-game.md)
- [Scroll an SMS Text Message on your RGB Matrix](https://learn.adafruit.com/scroll-an-sms-text-message-on-your-rgb-matrix.md)
- [Moon Phase Clock for Adafruit Matrix Portal](https://learn.adafruit.com/moon-phase-clock-for-adafruit-matrixportal.md)
- [Matrix Portal Scoreboard](https://learn.adafruit.com/matrix-portal-scoreboard.md)
- [Matrix Portal New Guide Scroller](https://learn.adafruit.com/matrix-portal-new-guide-scroller.md)
- [LED Matrix FIFA World Cup Scoreboard](https://learn.adafruit.com/led-matrix-fifa-world-cup-scoreboard.md)
- [LED Matrix Scoreboard](https://learn.adafruit.com/led-matrix-scoreboard.md)
- [RGB Matrix New York Times Title Scroller](https://learn.adafruit.com/rgb-matix-nyt-text-scroller.md)
- [MatrixPortal CircuitPython Animated Message Board](https://learn.adafruit.com/matrixportal-circuitpython-animated-message-board.md)
- [Ocean Epoxy Resin Lightbox with RGB LED Matrix Image Scroller](https://learn.adafruit.com/ocean-epoxy-resin-lightbox-with-rgb-led-matrix-image-scroller.md)
- [Blurry Analog Clock](https://learn.adafruit.com/blurry-analog-clock.md)
- [LED Matrix Sports Scoreboard](https://learn.adafruit.com/led-matrix-sports-scoreboard.md)
