Maker.io main logo

RGB LED Matrices with CircuitPython

359

2022-04-21 | By Adafruit Industries

License: See Original Project

Courtesy of Adafruit

Guide by Jeff Epler, Phillip Burgess, Lady Ada, Melissa LeBlanc-Williams

Overview

Bring a little bit of Times Square into your home with our RGB LED matrix panels. ‎These panels are normally used to make video walls — here in New York we see ‎them on the sides of buses and on bus stops — to display animations or short ‎video clips. We thought they looked really cool, so we picked up a few boxes from ‎the factory. They come in a variety of sizes from 16x32 pixels and up.‎

panels_1

Using the new RGBMatrix library, CircuitPython can blast pixels to these displays ‎really quickly. Use it with DisplayIO for showing text, bitmaps, animations, and ‎more. Not familiar with DisplayIO? There's a guide for that.‎

This guide is for select CircuitPython boards — ones based on the SAMD51 ‎‎(Feather M4, ItsyBitsy M4, Metro M4 etc) and nRF52840 (Feather nRF52840, ‎ItsyBitsy nRF52840, etc).‎

We have a different guide for Raspberry Pi and Arduino.‎

CircuitPython and Arduino share the same basic code for driving these matrices. ‎In CircuitPython, we call it RGBMatrix. In Arduino, it's named Protomatter.‎

rgb_2

pro_3

These panels require 12 or 13 digital pins (6 bit data, 6 or 7 bit control) and a ‎good 5V power supply, at least a couple amps per panel. We suggest our 2A (or ‎larger) regulated 5V adapters and either a terminal block DC jack or solder a jack ‎from our DC extension cord. Please read the rest of our tutorial for more details! ‎

Keep in mind that these displays are normally designed to be driven by FPGAs or ‎other high-speed processors; they do not have built in PWM control of any kind. ‎Instead, you're supposed to redraw the screen over and over to 'manually' PWM ‎the whole thing. RGBMatrix takes care of that for you, offering up to thousands ‎of bright colors. Depending on settings (width, height, and color depth) and ‎microcontroller, RGBMatrix takes from 10% to 60% of the processing power ‎away from CircuitPython.‎

display_4

Of course, we wouldn't leave you with a datasheet and a "good luck!" We have a ‎full wiring diagrams and working CircuitPython code.‎

Connecting with Feather M4 Express & FeatherWing

connect_5

The easiest way to get started with RGBMatrix is with the RGB Matrix ‎FeatherWing. Now you can quickly and easily create projects featuring your ‎favorite 16 or 32-pixel tall matrix boards.‎

Please note: This wing is only tested/designed to work with the SAMD51 M4 ‎Feather or the nRF52840 Feather. It may be possible to use the extra holes as a ‎prototyping area to adapt it to other Feathers, but that's beyond the scope of this ‎guide.‎

wing_6

This wing can be assembled in one of two ways. You can either solder in a 2x8 IDC ‎shrouded header on the top, then plug in the IDC cable that came with your ‎matrix. This makes it easy to stack on top of your Feather. Or you can solder in the ‎‎2x10 socket header on the bottom of the Wing, and then stack your Feather on ‎top. That way you can plug it directly into the back of the matrix *mind blown*.‎

wing_7

This FeatherWing will work great with any of our 16x32, 32x32, or 64x32 RGB ‎matrices, and is definitely the easiest way to glow and go.‎

wing_8

Either way you decide to go, you can plug a 5V DC power pack into the 2.1mm DC ‎jack. That 5V is polarity protected and then output on the other side to a 5.08mm ‎terminal block. An onboard regulator will provide 3.3V power to your Feather, so ‎you don't need a separate USB or battery. This makes for a very compact build!‎

Each 'Wing kit comes with one FeatherWing PCB with surface mount parts ‎attached, a 2x8 IDC header, a 2x10 female socket, 2.1mm DC jack, 5.08mm ‎terminal block, and some male header. You may also want some Feather stacking ‎headers or female headers depending on how you plan to attach/stack your ‎Feather.‎

Use the following block of code to initialize a 64x32 matrix.‎

Download File

Copy Code
displayio.release_displays()
matrix = rgbmatrix.RGBMatrix(
    width=64, bit_depth=4,
    rgb_pins=[board.D6, board.D5, board.D9, board.D11, board.D10, board.D12],
    addr_pins=[board.A5, board.A4, board.A3, board.A2],
    clock_pin=board.D13, latch_pin=board.D0, output_enable_pin=board.D1)
display = framebufferio.FramebufferDisplay(matrix)

Use the following block of code to initialize a 32x16 matrix. In this mode, you ‎may not use pin A2 unless you cut the trace connecting it to the header, because ‎it is connected to GND on the RGB matrix. ‎

Download File‎

Copy Code
displayio.release_displays()
matrix = rgbmatrix.RGBMatrix(
    width=32, bit_depth=4,
    rgb_pins=[board.D6, board.D5, board.D9, board.D11, board.D10, board.D12],
    addr_pins=[board.A5, board.A4, board.A3],
    clock_pin=board.D13, latch_pin=board.D0, output_enable_pin=board.D1)
display = framebufferio.FramebufferDisplay(matrix)

Connecting with Feather RP2040 & FeatherWing

connecting_9

The easiest way to get started with RGBMatrix is with the RGB Matrix ‎FeatherWing. Now you can quickly and easily create projects featuring your ‎favorite 16 or 32-pixel tall matrix boards.‎

Please note: This wing is only tested/designed to work with the SAMD51 M4 ‎Feather or the nRF52840 Feather. It may be possible to use the extra holes as a ‎prototyping area to adapt it to other Feathers, but that's beyond the scope of this ‎guide.‎

proto_10

This wing can be assembled in one of two ways. You can either solder in a 2x8 IDC ‎shrouded header on the top, then plug in the IDC cable that came with your ‎matrix. This makes it easy to stack on top of your Feather. Or you can solder in the ‎‎2x10 socket header on the bottom of the Wing, and then stack your Feather on ‎top. That way you can plug it directly into the back of the matrix *mind blown*.‎

assemble_11

This FeatherWing will work great with any of our 16x32, 32x32, or 64x32 RGB ‎matrices, and is definitely the easiest way to glow and go.‎

plug_12

Either way you decide to go, you can plug a 5V DC power pack into the 2.1mm DC ‎jack. That 5V is polarity protected and then output on the other side to a 5.08mm ‎terminal block. An onboard regulator will provide 3.3V power to your Feather, so ‎you don't need a separate USB or battery. This makes for a very compact build!‎

Each 'Wing kit comes with one FeatherWing PCB with surface mount parts ‎attached, a 2x8 IDC header, a 2x10 female socket, 2.1mm DC jack, 5.08mm ‎terminal block, and some male header. You may also want some Feather stacking ‎headers or female headers depending on how you plan to attach/stack your ‎Feather.‎

Use the following block of code to initialize a 64x32 matrix.‎

Download File

Copy Code
displayio.release_displays()
matrix = rgbmatrix.RGBMatrix(
    width=64, bit_depth=4,
    rgb_pins=[board.D6, board.D5, board.D9, board.D11, board.D10, board.D12],
    addr_pins=[board.D25, board.D24, board.A3, board.A2],
    clock_pin=board.D13, latch_pin=board.D0, output_enable_pin=board.D1)
display = framebufferio.FramebufferDisplay(matrix)

Use the following block of code to initialize a 32x16 matrix. In this mode, you ‎may not use pin A2 unless you cut the trace connecting it to the header, because ‎it is connected to GND on the RGB matrix. ‎

Download File‎

Copy Code
displayio.release_displays()
matrix = rgbmatrix.RGBMatrix(
    width=32, bit_depth=4,
    rgb_pins=[board.D6, board.D5, board.D9, board.D11, board.D10, board.D12],
    addr_pins=[board.D25, board.D24, board.A3],
    clock_pin=board.D13, latch_pin=board.D0, output_enable_pin=board.D1)
display = framebufferio.FramebufferDisplay(matrix)

With the RP2040, it is normal for the display to flicker while writing to the ‎CIRCUITPY drive, such as when updating your code.py file. It will not harm the ‎display.‎

Complete example: RP2040 Feather Scroller

Save the below file (called rp2040.py to CIRCUITPY as code.py and also save the ‎bitmap image file pi-logo32b.bmp to CIRCUITPY. The 64x32 matrix will scroll the ‎text "RP2040 Feather" over a Raspberry Pi logo.‎

‎Download Project Bundle‎

Copy Code
# SPDX-FileCopyrightText: 2021 Jeff Epler for Adafruit Industries
#
# SPDX-License-Identifier: MIT

import time
from math import sin
import board
import displayio
import rgbmatrix
import framebufferio
import adafruit_imageload
import terminalio
from adafruit_display_text.label import Label

displayio.release_displays()
matrix = rgbmatrix.RGBMatrix(
    width=64, bit_depth=6,
    rgb_pins=[board.D6, board.D5, board.D9, board.D11, board.D10, board.D12],
    addr_pins=[board.D25, board.D24, board.A3, board.A2],
    clock_pin=board.D13, latch_pin=board.D0, output_enable_pin=board.D1,
    doublebuffer=True)
display = framebufferio.FramebufferDisplay(matrix, auto_refresh=False)

g = displayio.Group()
b, p = adafruit_imageload.load("pi-logo32b.bmp")
t = displayio.TileGrid(b, pixel_shader=p)
t.x = 20
g.append(t)

l = Label(text="Feather\nRP2040", font=terminalio.FONT, color=0xffffff, line_spacing=.7)
g.append(l)

display.show(g)

target_fps = 50
ft = 1/target_fps
now = t0 = time.monotonic_ns()
deadline = t0 + ft

p = 1
q = 17
while True:
    tm = (now - t0) * 1e-9
    x = l.x - 1
    if x < -40:
        x = 63
    y =  round(12 + sin(tm / p) * 6)
    l.x = x
    l.y = y
    display.refresh(target_frames_per_second=target_fps, minimum_frames_per_second=0)
    while True:
        now = time.monotonic_ns()
        if now > deadline:
            break
        time.sleep((deadline - now) * 1e-9)
    deadline += ft

‎View on GitHub

Connecting Using a Proto Shield

Your LED matrix display needs 12 or 13 digital pins, and only specific combinations ‎will work. If this much custom wiring worries you, choose the Feather M4 Express ‎with an RGB Matrix Featherwing Kit, because just by connecting the headers you ‎get everything in the right place!‎

If you hold a ribbon cable flat — no folds — and with both connectors facing you, ‎keys pointed the same direction — there’s is a 1:1 correlation between the pins. ‎The top-right pin on one plug links to the top-right on the other plug, and so forth. ‎This holds true even if the cable has a doubled-over strain relief. As long as the ‎keys point the same way and the plugs face the same way, pins are in the same ‎positions at both ends.

ribbon_13

Either end of the ribbon cable can be plugged into the matrix INPUT socket.‎

The free end of the ribbon can point toward the center of the matrix or hang off ‎the side…the pinout is still the same. Notice below the direction of the “key” ‎doesn’t change.‎

free_14

A dual-row header gets installed on the prototyping area, similar to the connector ‎on the matrix. Just like the ribbon cable lying flat, as long as these two headers ‎are aligned the same way, they’ll match pin-for-pin.‎

pin_15

Wires are then soldered from the header to specific microcontroller pins. Try to ‎keep wire lengths reasonably short to avoid signal interference.‎

Using color-coded wires helps a lot! If you don’t have colored wires, that’s okay, ‎just pay close attention where everything goes. Our goal is a proto shield ‎something like this (but this is just an example; refer to the following pages for ‎specific information for your board):‎

color_16

You can also use a plain 2x8-pin male header, or two 1x8 sections installed side-‎by-side (as in the photo above). Since there’s no alignment key with this setup, ‎you might want to indicate it with some tape or a permanent marker.‎

Depending on the make and model of proto shield, some pins are designed to ‎connect in short rows. Others don’t. For the latter, strip a little extra insulation ‎and bend the wire to wrap around the leg of the socket from behind, then solder.‎

shield_17

Connect Ground Wires

32x32 and 64x32 matrices require three ground connections. 32x16 matrices ‎have four.‎‎ ‎

Most proto shields have tons of grounding points, so you shouldn’t have trouble ‎finding places to connect these.‎

wires_18

Upper RGB Data

Pins R1, G1, and B1 (labeled R0, B0 and G0 on some matrices) deliver data to ‎the top half of the display.‎ ‎

upper_19

Lower RGB Data

Pins R2, G2, and B2 (labeled R1, G1 and B1 on some matrices) deliver data to ‎the bottom half of the display. ‎

lower_20

Row Select Lines

Pins A, B, C, and D select which two rows of the display are currently lit. (32x16 ‎matrices don’t have a “D” pin — it’s connected to ground instead.)‎

select_21

LAT Wire

The LAT (latch) signal marks the end of a row of data.‎

lat_22

OE Wire

OE (output enable) switches the LEDs off when transitioning from one row to the ‎next.‎

oe_23

CLK Wire

Last one!‎

The CLK (clock) signal marks the arrival of each bit of data.‎

clk_24

Want to use a breadboard? We've got you covered too. With the Breakout ‎Helper, the 16-pin header can straddle that big gap in the middle.‎

Feather M4 Express

The easiest way to connect a LED matrix to the Feather M4 Express is using the ‎RGB Matrix FeatherWing kit. This board works only with the Feather M4 Express, ‎not other Feather boards like the nRF Feathers. If you've got the ‎FeatherWing, jump over to this page. Otherwise, read on and get wiring!‎

To hook an LED matrix to the Feather M4 Express, make the connections on the ‎left. Remember that for a 16-line matrix, connect 4 GND wires and address wires ‎A, B, and C. For a 32-line matrix, connect 3 GND wires and address wires A, B, C, ‎and D.‎

matrix_25

Use the following block of code to initialize a 64x32 matrix. Remember to connect ‎all 4 address pins: A, B, C, and D.‎

Don't forget you have to provide a separate 5V @ 2 ~ 10Amp power supply to the ‎panels thick power cables.‎

Download File

Copy Code
displayio.release_displays()
matrix = rgbmatrix.RGBMatrix(
    width=64, bit_depth=4,
    rgb_pins=[board.D6, board.D5, board.D9, board.D11, board.D10, board.D12],
    addr_pins=[board.A5, board.A4, board.A3, board.A2],
    clock_pin=board.D13, latch_pin=board.D0, output_enable_pin=board.D1)
display = framebufferio.FramebufferDisplay(matrix)

Use the following block of code to initialize a 32x16 matrix. Remember to connect just 3 address pins: A, B, and C. Hook a fourth GND wire instead of A3.

Download File

Copy Code
displayio.release_displays()
matrix = rgbmatrix.RGBMatrix(
    width=32, bit_depth=4,
    rgb_pins=[board.D6, board.D5, board.D9, board.D11, board.D10, board.D12],
    addr_pins=[board.A5, board.A4, board.A3],
    clock_pin=board.D13, latch_pin=board.D0, output_enable_pin=board.D1)
display = framebufferio.FramebufferDisplay(matrix)

Feather nRF52840‎

This suggested pinout was revised on 2020-06-03‎.

To hook an LED matrix to the Feather nRF52840 Express or Sense, make the ‎connections on the left. Remember that for a 16-line matrix, connect 4 GND wires ‎and address wires A, B, and C. For a 32-line matrix, connect 3 GND wires, and ‎address wires A, B, C, and D.‎

hook_26

Use the following block of code to initialize a 64x32 matrix. Remember to connect ‎all 4 address pins: A, B, C, and D.‎

Don't forget you have to provide a separate 5V @ 2 ~ 10Amp power supply to the ‎panels thick power cables.‎‎

Download File

Copy Code
displayio.release_displays()
matrix = rgbmatrix.RGBMatrix(
    width=64, bit_depth=4,
    rgb_pins=[board.D6, board.A5, board.A1, board.A0, board.A4, board.D11],
    addr_pins=[board.D10, board.D5, board.D13, board.D9],
    clock_pin=board.D12, latch_pin=board.RX, output_enable_pin=board.TX)
display = framebufferio.FramebufferDisplay(matrix)

Use the following block of code to initialize a 32x16 matrix. Remember to connect ‎just 3 address pins: A, B, and C. Hook a fourth GND wire instead of A3.‎

Download File

Copy Code
displayio.release_displays()
matrix = rgbmatrix.RGBMatrix(
    width=32, bit_depth=4,
    rgb_pins=[board.D6, board.A5, board.A1, board.A0, board.A4, board.D11],
    addr_pins=[board.D10, board.D5, board.D13],
    clock_pin=board.D12, latch_pin=board.RX, output_enable_pin=board.TX)
display = framebufferio.FramebufferDisplay(matrix)

ItsyBitsy M4‎

To hook an LED matrix to the ItsyBitsy M4, make the connections on the left. ‎Remember that for a 16-line matrix, connect 4 GND wires and address wires A, B, ‎and C. For a 32-line matrix, connect 3 GND wires and address wires A, B, C, and D.‎

led_27

Use the following block of code to initialize a 64x32 matrix. Remember to connect ‎all 4 address pins: A, B, C, and D.‎

Don't forget you have to provide a separate 5V @ 2 ~ 10Amp power supply to the ‎panels thick power cables.‎

Download File‎

Copy Code
displayio.release_displays()
matrix = rgbmatrix.RGBMatrix(
    width=64, bit_depth=4,
    rgb_pins=[board.MOSI, board.SCK, board.A5, board.A4, board.A0, board.A1],
    addr_pins=[board.D5, board.D7, board.D9, board.D10],
    clock_pin=board.D13, latch_pin=board.D12, output_enable_pin=board.D11)
display = framebufferio.FramebufferDisplay(matrix)

Use the following block of code to initialize a 32x16 matrix. Remember to connect ‎just 3 address pins: A, B, and C. Hook a fourth GND wire instead of A3.‎

Download File‎

Copy Code
displayio.release_displays()
matrix = rgbmatrix.RGBMatrix(
    width=32, bit_depth=4,
    rgb_pins=[board.MOSI, board.SCK, board.A5, board.A4, board.A0, board.A1],
    addr_pins=[board.D5, board.D7, board.D9],
    clock_pin=board.D13, latch_pin=board.D12, output_enable_pin=board.D11)
display = framebufferio.FramebufferDisplay(matrix)

ItsyBitsy nRF52840‎

To hook an LED matrix to the ItsyBitsy nRF52840, make the connections on the ‎left. Remember that for a 16-line matrix, connect 4 GND wires and address wires ‎A, B, and C. For a 32-line matrix, connect 3 GND wires and address wires A, B, C, ‎and D.‎

make_28

Use the following block of code to initialize a 64x32 matrix. Remember to connect ‎all 4 address pins: A, B, C, and D.‎

Don't forget you have to provide a separate 5V @ 2 ~ 10Amp power supply to the ‎panels thick power cables.‎‎

Download File

Copy Code
displayio.release_displays()
matrix = rgbmatrix.RGBMatrix(
    width=64, bit_depth=4,
    rgb_pins=[board.A3, board.A2, board.A1, board.RX, board.TX, board.D5],
    addr_pins=[board.D7, board.D9, board.D10, board.D12],
    clock_pin=board.D2, latch_pin=board.D11, output_enable_pin=board.MISO)
display = framebufferio.FramebufferDisplay(matrix)

Use the following block of code to initialize a 32x16 matrix. Remember to connect ‎just 3 address pins: A, B, and C. Hook a fourth GND wire instead of A3.‎

Download File

Copy Code
displayio.release_displays()
matrix = rgbmatrix.RGBMatrix(
    width=32, bit_depth=4,
    rgb_pins=[board.A3, board.A2, board.A1, board.RX, board.TX, board.D5],
    addr_pins=[board.D7, board.D9, board.D10],
    clock_pin=board.D2, latch_pin=board.D11, output_enable_pin=board.MISO)
display = framebufferio.FramebufferDisplay(matrix)

Metro M4 Express

To hook an LED matrix to the Metro M4 Express or Metro M4 Airlift Lite, make the ‎connections on the left. Remember that for a 16-line matrix, connect 4 GND wires ‎and address wires A, B, and C. For a 32-line matrix, connect 3 GND wires and ‎address wires A, B, C, and D.‎

hooked_29

Use the following block of code to initialize a 64x32 matrix. Remember to connect ‎all 4 address pins: A, B, C, and D.‎

Don't forget you have to provide a separate 5V @ 2 ~ 10Amp power supply to the ‎panels thick power cables.‎

Download File

Copy Code
displayio.release_displays()
matrix = rgbmatrix.RGBMatrix(
    width=64, bit_depth=4,
    rgb_pins=[board.D8, board.D9, board.D10, board.D11, board.D12, board.D13],
    addr_pins=[board.D4, board.D5, board.D6, board.D7],
    clock_pin=board.D1, latch_pin=board.D3, output_enable_pin=board.D2)
display = framebufferio.FramebufferDisplay(matrix)

Use the following block of code to initialize a 32x16 matrix. Remember to connect ‎just 3 address pins: A, B, and C. Hook a fourth GND wire instead of A3.‎

Download File‎

Copy Code
displayio.release_displays()
matrix = rgbmatrix.RGBMatrix(
    width=32, bit_depth=4,
    rgb_pins=[board.D8, board.D9, board.D10, board.D11, board.D12, board.D13],
    addr_pins=[board.D4, board.D5, board.D6],
    clock_pin=board.D1, latch_pin=board.D3, output_enable_pin=board.D2)
display = framebufferio.FramebufferDisplay(matrix)

Shields Up

If you're using the RGB Matrix Shield with the Metro M4 Express, you will need to ‎make a hardware modification.‎

shields_30

The RGB Matrix Shield is designed so that you can customize it to the ‎requirements of your board. We need to change which pin is used for the "CLK" ‎‎(clock) signal.‎

There is a small copper trace between the two larger pads. Using a razor blade, ‎cut or scrape away this trace. (Young people, please have an adult do this ‎step!) Then, use your multimeter in continuity mode to verify that there is no ‎connection between the two rectangular pads.‎

Next, make a connection from the A4 pin to the CLK pin by soldering in a new wire ‎as shown. The wire can be on the top or on the bottom.‎

rgb_32

rgb_31

Once that's done, use the following block of code to initialize a 64x32 matrix:‎

Download File

Copy Code
matrix = rgbmatrix.RGBMatrix(
    width=64,
    height=32,
    bit_depth=1,
    rgb_pins=[board.D2, board.D3, board.D4, board.D5, board.D6, board.D7],
    addr_pins=[board.A0, board.A1, board.A2, board.A3],
    clock_pin=board.A4,
    latch_pin=board.D10,
    output_enable_pin=board.D9,
)

Use the following block of code to initialize a 32x16 matrix:‎

Download File

Copy Code
matrix = rgbmatrix.RGBMatrix(
    width=32,
    height=16,
    bit_depth=1,
    rgb_pins=[board.D2, board.D3, board.D4, board.D5, board.D6, board.D7],
    addr_pins=[board.A0, board.A1, board.A2],
    clock_pin=board.A4,
    latch_pin=board.D10,
    output_enable_pin=board.D9,
)

Feather STM32F405‎

Support for this Feather is experimental, and you need to use CircuitPython 6.0.0-‎beta.3 or newer! If you get an ImportError, make sure you're using the right ‎version.‎

To hook an LED matrix to the Feather STM32F405, make the connections on the ‎left. Remember that for a 16-line matrix, connect 4 GND wires and address wires ‎A, B, and C. For a 32-line matrix, connect 3 GND wires and address wires A, B, C, ‎and D.‎

feather_33

Use the following block of code to initialize a 64x32 matrix. Remember to connect ‎all 4 address pins: A, B, C, and D.‎

Don't forget you have to provide a separate 5V @ 2 ~ 10Amp power supply to the ‎panels thick power cables.‎

‎Download File‎

Copy Code
displayio.release_displays()
matrix = rgbmatrix.RGBMatrix(
    width=64, bit_depth=4,
    rgb_pins=[board.D13, board.D12, board.D11, board.A4, board.A5, board.D6],
    addr_pins=[board.A0, board.A1, board.A2, board.A3],
    clock_pin=board.D5, latch_pin=board.D9, output_enable_pin=board.D10)
display = framebufferio.FramebufferDisplay(matrix)

Use the following block of code to initialize a 32x16 matrix. Remember to connect ‎just 3 address pins: A, B, and C. Hook a fourth GND wire instead of A3.‎

‎Download File‎

Copy Code
displayio.release_displays()
matrix = rgbmatrix.RGBMatrix(
    width=32, bit_depth=4,
    rgb_pins=[board.D13, board.D12, board.D11, board.A4, board.A5, board.D6],
    addr_pins=[board.A0, board.A1, board.A2],
    clock_pin=board.D5, latch_pin=board.D9, output_enable_pin=board.D10)
display = framebufferio.FramebufferDisplay(matrix)

Metro ESP32S2‎

metro_34

A great way to get started with RGBMatrix is with the RGB Matrix Shield and ‎the Metro ESP32-S2. Now you can quickly and easily create projects featuring ‎your favorite 16 or 32-pixel tall matrix boards.‎

For a small panel, you can use the screw terminals on the Adafruit RGB Matrix ‎Shield together with an appropriate DC power adapter on the Metro. For a larger ‎panel, you'll need an independent 5V supply at several amps.‎

In any case, using USB power alone is not recommended, as the RGB Matrix can ‎draw a lot of current.‎

Each Shield kit comes with one Shield PCB, a 2x8 IDC header, 5.08mm terminal ‎block, and some male header.‎

Use the following block of code to initialize a 64x32 matrix.‎

Download File

Copy Code
displayio.release_displays()
matrix = rgbmatrix.RGBMatrix(
    width=32,
    bit_depth=6,
    rgb_pins=[board.IO7, board.IO8, board.IO9, board.IO10, board.IO11, board.IO12],
    addr_pins=[board.A0, board.A1, board.A2, board.A3],
    clock_pin=board.IO13,
    latch_pin=board.IO15,
    output_enable_pin=board.IO14,
)
display = framebufferio.FramebufferDisplay(matrix)

Use the following block of code to initialize a 32x16 matrix. In this mode, you ‎may not use pin A3 unless you cut the trace connecting it to the header, because ‎it is connected to GND on the RGB matrix. ‎

Download File

Copy Code
displayio.release_displays()
matrix = rgbmatrix.RGBMatrix(
    width=32,
    bit_depth=4,
    rgb_pins=[board.IO7, board.IO8, board.IO9, board.IO10, board.IO11, board.IO12],
    addr_pins=[board.A0, board.A1, board.A2],
    clock_pin=board.IO13,
    latch_pin=board.IO15,
    output_enable_pin=board.IO14,
)
display = framebufferio.FramebufferDisplay(matrix)

Connecting Using a MatrixPortal

To hook an LED matrix to the MatrixPortal M4 board, just connect the power ‎wires and press it on. Check out the Prep the MatrixPortal page for more ‎detailed information. You can learn more about using the MatrixPortal in ‎our Adafruit MatrixPortal M4 guide.‎

portal_35

‎64x32 Matrix‎

Use the following block of code to initialize a 64x32 matrix.‎

‎Download File‎

Copy Code
displayio.release_displays()
matrix = rgbmatrix.RGBMatrix(
    width=64, bit_depth=4,
    rgb_pins=[
        board.MTX_R1,
        board.MTX_G1,
        board.MTX_B1,
        board.MTX_R2,
        board.MTX_G2,
        board.MTX_B2
    ],
    addr_pins=[
        board.MTX_ADDRA,
        board.MTX_ADDRB,
        board.MTX_ADDRC,
        board.MTX_ADDRD
    ],
    clock_pin=board.MTX_CLK,
    latch_pin=board.MTX_LAT,
    output_enable_pin=board.MTX_OE
)
display = framebufferio.FramebufferDisplay(matrix)

‎32x16 Matrix‎

Use the following block of code to initialize a 32x16 matrix.‎

‎Download File

Copy Code
displayio.release_displays()
matrix = rgbmatrix.RGBMatrix(
    width=32, bit_depth=4,
    rgb_pins=[
        board.MTX_R1,
        board.MTX_G1,
        board.MTX_B1,
        board.MTX_R2,
        board.MTX_G2,
        board.MTX_B2
    ],
    addr_pins=[
        board.MTX_ADDRA,
        board.MTX_ADDRB,
        board.MTX_ADDRC
    ],
    clock_pin=board.MTX_CLK,
    latch_pin=board.MTX_LAT,
    output_enable_pin=board.MTX_OE
)
display = framebufferio.FramebufferDisplay(matrix)

‎64x64 Matrix‎

Use the following block of code to initialize a 64x64 matrix. Don't forget to close ‎the Address E Line jumper with solder. You'll need to check the datasheet for ‎your matrix to determine whether to connect it to Pin 8 or 16.‎

Don't forget to close the Address E Line jumper first.‎

Download File

Copy Code
displayio.release_displays()
matrix = rgbmatrix.RGBMatrix(
    width=64, height=64, bit_depth=4,
    rgb_pins=[
        board.MTX_R1,
        board.MTX_G1,
        board.MTX_B1,
        board.MTX_R2,
        board.MTX_G2,
        board.MTX_B2
    ],
    addr_pins=[
        board.MTX_ADDRA,
        board.MTX_ADDRB,
        board.MTX_ADDRC,
        board.MTX_ADDRD,
        board.MTX_ADDRE
    ],
    clock_pin=board.MTX_CLK,
    latch_pin=board.MTX_LAT,
    output_enable_pin=board.MTX_OE
)
display = framebufferio.FramebufferDisplay(matrix)

Prep the MatrixPortal

Power Prep

The MatrixPortal supplies power to the matrix display panel via two standoffs. ‎These come with protective tape applied (part of our manufacturing process) ‎which MUST BE REMOVED!‎

Use some tweezers or a fingernail to remove the two amber circles.‎

power_36

power_37

Power Terminals

Next, screw in the spade connectors to the corresponding standoff.‎

  • red wire goes to +5V

  • black wire goes to GND

screw_38

screw_39

Panel Power

Plug either one of the four-conductor power plugs into the power connector pins ‎on the panel. The plug can only go in one way, and that way is marked on the ‎board's silkscreen.‎

panel_40

panel_41

Board Connection

Now, plug the board into the left side shrouded 8x2 connector as shown. The ‎orientation matters, so take a moment to confirm that the white indicator arrow ‎on the matrix panel is oriented pointing up and right as seen here and the ‎MatrixPortal overhangs the edge of the panel when connected. This allows you to ‎use the edge buttons from the front side.‎

Check nothing is impeding the board from plugging in firmly. If there's a plastic ‎nub on the matrix that's keeping the Portal from sitting flat, cut it off with ‎diagonal cutters.‎

board_42

board_43

check_44

check_45

For info on adding LED diffusion acrylic, see the page LED Matrix Diffuser.‎

Example: Simple two-line text scroller

scroll_46

This example creates two lines of scrolling text on a 64x32 matrix ‎using CircuitPython Display Text. It's designed for the FeatherWing M4 Express, ‎but you can adapt it to other boards by changing the lines that create the ‎RGBMatrix object. We've thoroughly commented this example so it's a great place ‎to start if you're not familiar with displayio.‎

For this example, make sure to install the following library:‎

If you want to customize the example to use a different font, you'll also need

Here's how the code will work:‎

  • Create the RGBMatrix and FramebufferDisplay objects

  • Create the two text labels to scroll

  • Put them in a Group, and then show that Group

  • Repeatedly scroll each label to the left, returning it to the far-right hand side ‎when it has gone all the way

‎Download Project Bundle‎

Copy Code
# SPDX-FileCopyrightText: 2020 Jeff Epler for Adafruit Industries
#
# SPDX-License-Identifier: MIT

# This example implements a simple two line scroller using
# Adafruit_CircuitPython_Display_Text. Each line has its own color
# and it is possible to modify the example to use other fonts and non-standard
# characters.

import adafruit_display_text.label
import board
import displayio
import framebufferio
import rgbmatrix
import terminalio

# If there was a display before (protomatter, LCD, or E-paper), release it so
# we can create ours
displayio.release_displays()

# This next call creates the RGB Matrix object itself. It has the given width
# and height. bit_depth can range from 1 to 6; higher numbers allow more color
# shades to be displayed, but increase memory usage and slow down your Python
# code. If you just want to show primary colors plus black and white, use 1.
# Otherwise, try 3, 4 and 5 to see which effect you like best.
#
# These lines are for the Feather M4 Express. If you're using a different board,
# check the guide to find the pins and wiring diagrams for your board.
# If you have a matrix with a different width or height, change that too.
# If you have a 16x32 display, try with just a single line of text.
matrix = rgbmatrix.RGBMatrix(
    width=64, height=32, bit_depth=1,
    rgb_pins=[board.D6, board.D5, board.D9, board.D11, board.D10, board.D12],
    addr_pins=[board.A5, board.A4, board.A3, board.A2],
    clock_pin=board.D13, latch_pin=board.D0, output_enable_pin=board.D1)

# Associate the RGB matrix with a Display so that we can use displayio features
display = framebufferio.FramebufferDisplay(matrix, auto_refresh=False)

# Create two lines of text to scroll. Besides changing the text, you can also
# customize the color and font (using Adafruit_CircuitPython_Bitmap_Font).
# To keep this demo simple, we just used the built-in font.
# The Y coordinates of the two lines were chosen so that they looked good
# but if you change the font you might find that other values work better.
line1 = adafruit_display_text.label.Label(
    terminalio.FONT,
    color=0xff0000,
    text="This scroller is brought to you by CircuitPython RGBMatrix")
line1.x = display.width
line1.y = 8

line2 = adafruit_display_text.label.Label(
    terminalio.FONT,
    color=0x0080ff,
    text="Hello to all CircuitPython contributors worldwide <3")
line2.x = display.width
line2.y = 24

# Put each line of text into a Group, then show that group.
g = displayio.Group()
g.append(line1)
g.append(line2)
display.show(g)

# This function will scoot one label a pixel to the left and send it back to
# the far right if it's gone all the way off screen. This goes in a function
# because we'll do exactly the same thing with line1 and line2 below.
def scroll(line):
    line.x = line.x - 1
    line_width = line.bounding_box[2]
    if line.x < -line_width:
        line.x = display.width

# This function scrolls lines backwards.  Try switching which function is
# called for line2 below!
def reverse_scroll(line):
    line.x = line.x + 1
    line_width = line.bounding_box[2]
    if line.x >= display.width:
        line.x = -line_width

# You can add more effects in this loop. For instance, maybe you want to set the
# color of each label to a different value.
while True:
    scroll(line1)
    scroll(line2)
    #reverse_scroll(line2)
    display.refresh(minimum_frames_per_second=0)

View on GitHub

Example: Two-line colorful text scroller

example_47

This example creates two lines of scrolling text on a 64x32 matrix. It's designed for ‎the FeatherWing M4 Express, but you can adapt it to other boards by changing ‎the lines that create the RGBMatrix object.‎

Because each letter is set to a different color, this example doesn't use the ‎adafruit_display_text library.‎

To customize the text, simply make sure you keep the lines in pairs of equal length, ‎and don't miss the comma at the end of each line.‎

Download Project Bundle‎

Copy Code
# SPDX-FileCopyrightText: 2020 Jeff Epler for Adafruit Industries
#
# SPDX-License-Identifier: MIT

# This example implements a rainbow colored scroller, in which each letter
# has a different color. This is not possible with
# Adafruit_Circuitpython_Display_Text, where each letter in a label has the
# same color
#
# This demo also supports only ASCII characters and the built-in font.
# See the simple_scroller example for one that supports alternative fonts
# and characters, but only has a single color per label.

import array

from rainbowio import colorwheel
import board
import displayio
import framebufferio
import rgbmatrix
import terminalio
displayio.release_displays()

matrix = rgbmatrix.RGBMatrix(
    width=64, height=32, bit_depth=3,
    rgb_pins=[board.D6, board.D5, board.D9, board.D11, board.D10, board.D12],
    addr_pins=[board.A5, board.A4, board.A3, board.A2],
    clock_pin=board.D13, latch_pin=board.D0, output_enable_pin=board.D1)
display = framebufferio.FramebufferDisplay(matrix, auto_refresh=False)

# Create a tilegrid with a bunch of common settings
def tilegrid(palette):
    return displayio.TileGrid(
        bitmap=terminalio.FONT.bitmap, pixel_shader=palette,
        width=1, height=1, tile_width=6, tile_height=14, default_tile=32)

g = displayio.Group()

# We only use the built in font which we treat as being 7x14 pixels
linelen = (64//7)+2

# prepare the main groups
l1 = displayio.Group()
l2 = displayio.Group()
g.append(l1)
g.append(l2)
display.show(g)

l1.y = 1
l2.y = 16

# Prepare the palettes and the individual characters' tiles
sh = [displayio.Palette(2) for _ in range(linelen)]
tg1 = [tilegrid(shi) for shi in sh]
tg2 = [tilegrid(shi) for shi in sh]

# Prepare a fast map from byte values to
charmap = array.array('b', [terminalio.FONT.get_glyph(32).tile_index]) * 256
for ch in range(256):
    glyph = terminalio.FONT.get_glyph(ch)
    if glyph is not None:
        charmap[ch] = glyph.tile_index

# Set the X coordinates of each character in label 1, and add it to its group
for idx, gi in enumerate(tg1):
    gi.x = 7 * idx
    l1.append(gi)

# Set the X coordinates of each character in label 2, and add it to its group
for idx, gi in enumerate(tg2):
    gi.x = 7 * idx
    l2.append(gi)

#  These pairs of lines should be the same length
lines = [
    b"This scroller is brought to you by    CircuitPython & PROTOMATTER",
    b"        .... . .-.. .-.. --- / .--. .-. --- - --- -- .- - - . .-.",
    b"Greetz to ...          @PaintYourDragon      @v923z  @adafruit         ",
    b"  @danh        @ladyada  @kattni      @tannewt    all showers & tellers",
    b"New York Strong                       Wash Your Hands                  ",
    b"                  Flatten the curve                   Stronger Together",
]

even_lines = lines[0::2]
odd_lines = lines[1::2]

# Scroll a top text and a bottom text
def scroll(t, b):
    # Add spaces to the start and end of each label so that it goes from
    # the far right all the way off the left
    sp = b' ' * linelen
    t = sp + t + sp
    b = sp + b + sp
    maxlen = max(len(t), len(b))
    # For each whole character position...
    for i in range(maxlen-linelen):
        # Set the letter displayed at each position, and its color
        for j in range(linelen):
            sh[j][1] = colorwheel(3 * (2*i+j))
            tg1[j][0] = charmap[t[i+j]]
            tg2[j][0] = charmap[b[i+j]]
        # And then for each pixel position, move the two labels
        # and then refresh the display.
        for j in range(7):
            l1.x = -j
            l2.x = -j
            display.refresh(minimum_frames_per_second=0)

# Repeatedly scroll all the pairs of lines
while True:
    for e, o in zip(even_lines, odd_lines):
        scroll(e, o)

‎View on GitHub

Example: (Ada)Fruit Machine

 

 

simulate_48

This example simulates a "fruit machine", similar to a slot machine, using emoji ‎from the Adafruit Discord server. It's designed for the FeatherWing M4 Express, ‎but you can adapt it to other boards by changing the lines that create the ‎RGBMatrix object. The code is also designed for the 64x32 LED displays.‎

You'll also need the emoji.bmp file on your CIRCUITPY drive.‎

Download Project Bundle‎

Copy Code
# SPDX-FileCopyrightText: 2020 Jeff Epler for Adafruit Industries
#
# SPDX-License-Identifier: MIT

import random
import time

import board
import displayio
import framebufferio
import rgbmatrix

displayio.release_displays()

matrix = rgbmatrix.RGBMatrix(
    width=64, height=32, bit_depth=3,
    rgb_pins=[board.D6, board.D5, board.D9, board.D11, board.D10, board.D12],
    addr_pins=[board.A5, board.A4, board.A3, board.A2],
    clock_pin=board.D13, latch_pin=board.D0, output_enable_pin=board.D1)
display = framebufferio.FramebufferDisplay(matrix, auto_refresh=False)

# This bitmap contains the emoji we're going to use. It is assumed
# to contain 20 icons, each 20x24 pixels. This fits nicely on the 64x32
# RGB matrix display.

filename = "emoji.bmp"

# CircuitPython 6 & 7 compatible
bitmap_file = open(filename, 'rb')
bitmap = displayio.OnDiskBitmap(bitmap_file)
pixel_shader = getattr(bitmap, 'pixel_shader', displayio.ColorConverter())

# # CircuitPython 7+ compatible
# bitmap = displayio.OnDiskBitmap(filename)
# pixel_shader = bitmap.pixel_shader

# Each wheel can be in one of three states:
STOPPED, RUNNING, BRAKING = range(3)

# Return a duplicate of the input list in a random (shuffled) order.
def shuffled(seq):
    return sorted(seq, key=lambda _: random.random())

# The Wheel class manages the state of one wheel. "pos" is a position in
# scaled integer coordinates, with one revolution being 7680 positions
# and 1 pixel being 16 positions. The wheel also has a velocity (in positions
# per tick) and a state (one of the above constants)
class Wheel(displayio.TileGrid):
    def __init__(self):
        # Portions of up to 3 tiles are visible.
        super().__init__(bitmap=bitmap, pixel_shader=pixel_shader,
                         width=1, height=3, tile_width=20, tile_height=24)
        self.order = shuffled(range(20))
        self.state = STOPPED
        self.pos = 0
        self.vel = 0
        self.y = 0
        self.x = 0
        self.stop_time = time.monotonic_ns()

    def step(self):
        # Update each wheel for one time step
        if self.state == RUNNING:
            # Slowly lose speed when running, but go at least speed 64
            self.vel = max(self.vel * 9 // 10, 64)
            if time.monotonic_ns() > self.stop_time:
                self.state = BRAKING
        elif self.state == BRAKING:
            # More quickly lose speed when braking, down to speed 7
            self.vel = max(self.vel * 85 // 100, 7)

        # Advance the wheel according to the velocity, and wrap it around
        # after 7680 positions
        self.pos = (self.pos + self.vel) % 7680

        # Compute the rounded Y coordinate
        yy = round(self.pos / 16)
        # Compute the offset of the tile (tiles are 24 pixels tall)
        yyy = yy % 24
        # Find out which tile is the top tile
        off = yy // 24

        # If we're braking and a tile is close to midscreen,
        # then stop and make sure that tile is exactly centered
        if self.state == BRAKING and self.vel == 7 and yyy < 4:
            self.pos = off * 24 * 16
            self.vel = 0
            self.state = STOPPED

        # Move the displayed tiles to the correct height and make sure the
        # correct tiles are displayed.
        self.y = yyy - 20
        for i in range(3):
            self[i] = self.order[(19 - i + off) % 20]

    # Set the wheel running again, using a slight bit of randomness.
    # The 'i' value makes sure the first wheel brakes first, the second
    # brakes second, and the third brakes third.
    def kick(self, i):
        self.state = RUNNING
        self.vel = random.randint(256, 320)
        self.stop_time = time.monotonic_ns() + 3_000_000_000 + i * 350_000_000

# Our fruit machine has 3 wheels, let's create them with a correct horizontal
# (x) offset and arbitrary vertical (y) offset.
g = displayio.Group()
wheels = []
for idx in range(3):
    wheel = Wheel()
    wheel.x = idx * 22
    wheel.y = -20
    g.append(wheel)
    wheels.append(wheel)
display.show(g)

# Make a unique order of the emoji on each wheel
orders = [shuffled(range(20)), shuffled(range(20)), shuffled(range(20))]

# And put up some images to start with
for si, oi in zip(wheels, orders):
    for idx in range(3):
        si[idx] = oi[idx]

# We want a way to check if all the wheels are stopped
def all_stopped():
    return all(si.state == STOPPED for si in wheels)

# To start with, though, they're all in motion
for idx, si in enumerate(wheels):
    si.kick(idx)

# Here's the main loop
while True:
    # Refresh the display (doing this manually ensures the wheels move
    # together, not at different times)
    display.refresh(minimum_frames_per_second=0)
    if all_stopped():
        # Once everything comes to a stop, wait a little bit and then
        # start everything over again.  Maybe you want to check if the
        # combination is a "winner" and add a light show or something.
        for idx in range(100):
            display.refresh(minimum_frames_per_second=0)
        for idx, si in enumerate(wheels):
            si.kick(idx)

    # Otherwise, let the wheels keep spinning...
    for idx, si in enumerate(wheels):
        si.step()

‎View on GitHub

Example: Conway's "Game of Life"‎

 

 

According to Wikipedia, "John Horton Conway was an English mathematician ‎active in the theory of finite groups, knot theory, number theory, combinatorial ‎game theory, and coding theory. He also made contributions to many branches of ‎recreational mathematics, most notably the invention of the cellular automaton ‎called the Game of Life."‎

Web comic XKCD memorialized his passing with a comic which showed a stick ‎person turning into a "glider", a construct in the Game of Life which will continue ‎moving indefinitely into empty space.‎

This example opens with a recreation of XKCD's tribute to Conway, then from ‎time to time refreshes the display with a random state. It's designed for the ‎FeatherWing M4 Express, but you can adapt it to other boards by changing the ‎lines that create the RGBMatrix object. The code is also designed for the 64x32 ‎LED displays. Unlike the other demos, it will adapt to other display sizes like 16x32 ‎by changing the lines that create the RGBMatrix object.‎

Download Project Bundle‎

Copy Code
# SPDX-FileCopyrightText: 2020 Jeff Epler for Adafruit Industries
#
# SPDX-License-Identifier: MIT

import random
import time

import board
import displayio
import framebufferio
import rgbmatrix

displayio.release_displays()

# Conway's "Game of Life" is played on a grid with simple rules, based
# on the number of filled neighbors each cell has and whether the cell itself
# is filled.
#   * If the cell is filled, and 2 or 3 neighbors are filled, the cell stays
#     filled
#   * If the cell is empty, and exactly 3 neighbors are filled, a new cell
#     becomes filled
#   * Otherwise, the cell becomes or remains empty
#
# The complicated way that the "m1" (minus 1) and "p1" (plus one) offsets are
# calculated is due to the way the grid "wraps around", with the left and right
# sides being connected, as well as the top and bottom sides being connected.
#
# This function has been somewhat optimized, so that when it indexes the bitmap
# a single number [x + width * y] is used instead of indexing with [x, y].
# This makes the animation run faster with some loss of clarity. More
# optimizations are probably possible.

def apply_life_rule(old, new):
    width = old.width
    height = old.height
    for y in range(height):
        yyy = y * width
        ym1 = ((y + height - 1) % height) * width
        yp1 = ((y + 1) % height) * width
        xm1 = width - 1
        for x in range(width):
            xp1 = (x + 1) % width
            neighbors = (
                old[xm1 + ym1] + old[xm1 + yyy] + old[xm1 + yp1] +
                old[x   + ym1] +                  old[x   + yp1] +
                old[xp1 + ym1] + old[xp1 + yyy] + old[xp1 + yp1])
            new[x+yyy] = neighbors == 3 or (neighbors == 2 and old[x+yyy])
            xm1 = x

# Fill 'fraction' out of all the cells.
def randomize(output, fraction=0.33):
    for i in range(output.height * output.width):
        output[i] = random.random() < fraction


# Fill the grid with a tribute to John Conway
def conway(output):
    # based on xkcd's tribute to John Conway (1937-2020) https://xkcd.com/2293/
    conway_data = [
        b'  +++   ',
        b'  + +   ',
        b'  + +   ',
        b'   +    ',
        b'+ +++   ',
        b' + + +  ',
        b'   +  + ',
        b'  + +   ',
        b'  + +   ',
    ]
    for i in range(output.height * output.width):
        output[i] = 0
    for i, si in enumerate(conway_data):
        y = output.height - len(conway_data) - 2 + i
        for j, cj in enumerate(si):
            output[(output.width - 8)//2 + j, y] = cj & 1

# bit_depth=1 is used here because we only use primary colors, and it makes
# the animation run a bit faster because RGBMatrix isn't taking over the CPU
# as often.
matrix = rgbmatrix.RGBMatrix(
    width=64, height=32, bit_depth=1,
    rgb_pins=[board.D6, board.D5, board.D9, board.D11, board.D10, board.D12],
    addr_pins=[board.A5, board.A4, board.A3, board.A2],
    clock_pin=board.D13, latch_pin=board.D0, output_enable_pin=board.D1)
display = framebufferio.FramebufferDisplay(matrix, auto_refresh=False)
SCALE = 1
b1 = displayio.Bitmap(display.width//SCALE, display.height//SCALE, 2)
b2 = displayio.Bitmap(display.width//SCALE, display.height//SCALE, 2)
palette = displayio.Palette(2)
tg1 = displayio.TileGrid(b1, pixel_shader=palette)
tg2 = displayio.TileGrid(b2, pixel_shader=palette)
g1 = displayio.Group(scale=SCALE)
g1.append(tg1)
display.show(g1)
g2 = displayio.Group(scale=SCALE)
g2.append(tg2)

# First time, show the Conway tribute
palette[1] = 0xffffff
conway(b1)
display.auto_refresh = True
time.sleep(3)
n = 40

while True:
    # run 2*n generations.
    # For the Conway tribute on 64x32, 80 frames is appropriate.  For random
    # values, 400 frames seems like a good number.  Working in this way, with
    # two bitmaps, reduces copying data and makes the animation a bit faster
    for _ in range(n):
        display.show(g1)
        apply_life_rule(b1, b2)
        display.show(g2)
        apply_life_rule(b2, b1)

    # After 2*n generations, fill the board with random values and
    # start over with a new color.
    randomize(b1)
    # Pick a random color out of 6 primary colors or white.
    palette[1] = (
        (0x0000ff if random.random() > .33 else 0) |
        (0x00ff00 if random.random() > .33 else 0) |
        (0xff0000 if random.random() > .33 else 0)) or 0xffffff
    n = 200

‎View on GitHub

Advanced: Multiple Panels

The features on this page require CircuitPython 6.2 or later (including 6.2.0-‎alpha.1)‎

If you have multiple identical panels, you can extend a single display across them, ‎vertically, horizontally, or both.‎

back_49

Back view of four 64x32 panels in a 2x2 arrangement. The MatrixPortal controller ‎is at top left.‎

Inspect the rear of your panel. There are two 10-pin connectors. You can "chain" ‎multiple displays by using a ribbon cable to hook the "OUT" of one panel to the ‎‎"IN" of the next panel.‎

Many of Adafruit's panels include a ribbon cable; check the description.‎

panels_50

Next, you'll need to power all your panels. Each panel has a 4-pin power ‎connector, and most of Adafruit's panels include a Y-splitter power cable which ‎can be connected to 2 panels. The power supply current requirement increases ‎according to the number of panels, but the voltage requirement is always 5V. ‎Remember that we recommend a 5V 4A supply for a single 64x32 pixel panel, so ‎our recommendation becomes 4×2=8A for 2 panels and 4×4=16A for 4 panels. ‎By using dimmer colors and avoiding big solid areas on the display, you may be ‎able to get by with a smaller power supply.‎

An insufficient power supply is frequent problem and can lead to flickering and ‎jumbled displays.‎

More displays also mean more memory (RAM) usage. Using bit_depth=6, ‎approximate RAM usage is as follows:‎

  • one 64×32 panel uses 17kB

  • two 64×32 panels use 33kB

  • four 64×32 panels use 65kB

Using bit_depth=3, approximate RAM usage is as follows:‎

  • one 64×32 panel uses 11kB

  • two 64×32 panels use 21kB

  • four 64×32 panels use 42kB

Using bit_depth=1, approximate RAM usage is as follows:‎

  • one 64×32 panel uses 7kB

  • two 64×32 panels use 13kB

  • four 64×32 panels use 25kB

The total number of panels is limited by RAM usage, CPU usage, and refresh rate. ‎Other arrangements than those shown, such as 1×3, 3×1, 2x3, 3x2, etc., are ‎possible.‎

Chaining and Tiling

This script for MatrixPortal will be used to demonstrate the different chaining and ‎tiling options:‎

Download File‎

Copy Code
import displayio
import board
import rgbmatrix
import framebufferio

bit_depth = 1
base_width = 64
base_height = 32
chain_across = 1
tile_down = 1
serpentine = True

width = base_width * chain_across
height = base_height * tile_down

addr_pins = [board.MTX_ADDRA, board.MTX_ADDRB, board.MTX_ADDRC, board.MTX_ADDRD]
rgb_pins = [
    board.MTX_R1,
    board.MTX_G1,
    board.MTX_B1,
    board.MTX_R2,
    board.MTX_G2,
    board.MTX_B2,
]
clock_pin = board.MTX_CLK
latch_pin = board.MTX_LAT
oe_pin = board.MTX_OE

displayio.release_displays()
matrix = rgbmatrix.RGBMatrix(
                width=width,
                height=height,
                bit_depth=bit_depth,
                rgb_pins=rgb_pins,
                addr_pins=addr_pins,
                clock_pin=clock_pin,
                latch_pin=latch_pin,
                output_enable_pin=oe_pin,
                tile=tile_down, serpentine=serpentine,
            )
display = framebufferio.FramebufferDisplay(matrix)

We'll focus on the effect of these 3 settings:‎

  • chain_across

  • tile_down

  • serpentine

Note how chain_across is used to calculate the width from the base_width and ‎similarly that tile_down is used to calculate the height from the base_height.‎

The total number of displays is chain_across * tile_down.‎

The top-right panel (before rotation) is always the one that has the MatrixPortal, ‎or incoming ribbon cable attached to it.‎

Purely horizontal arrangements

  • chain_across = 1

  • tile_down = 1

  • serpentine doesn't matter

Uses a single panel, size is 64x32.‎

arrange_51

  • chain_across = 2

  • tile_down = 1

  • serpentine doesn't matter

Uses two panels, total display size is 128x32.‎

arrange_52

 

  • chain_across = 4

  • tile_down = 1

  • serpentine doesn't matter

Uses four panels, total display size is 256x32.‎

arrange_53

Purely vertical arrangements

The value called "serpentine" controls whether every alternate row of panels is ‎rotated 180 degrees. This is generally more convenient, due to the positions of ‎the connectors. The approximate path of the ribbon cables is shown with the ‎arrows.‎

  • chain_across = 1‎

  • tile_down = 4

  • serpentine = True

Uses four panels, total display size is 64x128. Notice how displays 2 and 4 are ‎rotated 180 degrees to minimize the length of ribbon cables required.‎

vert_54

  • chain_across = 1

  • tile_down = 4‎

  • serpentine = False

Uses four panels, total display size is 64x128. Note how the ribbon cables are ‎longer than with serpentine=True. This is usually less convenient.‎

vert_55

Rectangular arrangements

  • chain_across = 2

  • tile_down = 2‎

  • serpentine = True

Uses four panels, total display size is 128x64. Note how displays 3 and 4 are ‎rotated 180 degrees to minimize the length of ribbon cables required.‎

rec_56

  • chain_across = 2

  • tile_down = 2‎

  • serpentine = False

Uses four panels, total display size is 128x64.‎

Note how long the ribbon cable is from panel 2 to 3. This is why the "serpentine" ‎arrangement is usual preferable to the non-serpentine arrangement.‎

rec_57

The effect of display rotation

All of the above examples show a display with rotation=0. You can change the ‎rotation of a screen by passing in a rotation= parameter to ‎the FramebufferDisplay constructor, or by setting the .rotation property of ‎the display object at any time.‎

This rotation, which can be any of 0, 90, 180, or 270 degrees counterclockwise, ‎affects the whole display rather than the individual panels.‎

display.rotation = 90‎

display_58

display.rotation = 180‎

display_59

display.rotation = 270

display_60

Advanced Example: Big, big flag of Wales

dragon_61

This example uses 4 64×32 matrices attached to a single MatrixPortal using the ‎‎"serpentine" wiring style.‎

Place the code below on CIRCUITPY (as code.py) and the bitmap ‎image (as wales.bmp). You can change the bitmap to any other file that works ‎with OnDiskBitmap, up to 128x64 pixels. No additional libraries are required. You ‎can adapt it to other boards by changing the lines that create ‎the RGBMatrix object.‎

object_62

object_63

Download Project Bundle

Copy Code
# SPDX-FileCopyrightText: 2021 Jeff Epler for Adafruit Industries
#
# SPDX-License-Identifier: MIT

# Minimal example displaying an image tiled across multiple RGB LED matrices.
# This is written for MatrixPortal and four 64x32 pixel matrices, but could
# be adapted to different boards and matrix combinations.
# No additional libraries required, just uses displayio.
# Image wales.bmp should be in CIRCUITPY root directory.

import board
import displayio
import framebufferio
import rgbmatrix

displayio.release_displays() # Release current display, we'll create our own

# Create RGB matrix object for a chain of four 64x32 matrices tiled into
# a single 128x64 pixel display -- two matrices across, two down, with the
# second row being flipped. width and height args are the combined size of
# all the tiled sub-matrices. tile arg is the number of rows of matrices in
# the chain (horizontal tiling is implicit from the width argument, doesn't
# need to be specified, but vertical tiling must be explicitly stated).
# The serpentine argument indicates whether alternate rows are flipped --
# cabling is easier this way, downside is colors may be slightly different
# when viewed off-angle. bit_depth and pins are same as other examples.
MATRIX = rgbmatrix.RGBMatrix(
    width=128, height=64, bit_depth=6, tile=2, serpentine=True,
    rgb_pins=[board.MTX_R1,
              board.MTX_G1,
              board.MTX_B1,
              board.MTX_R2,
              board.MTX_G2,
              board.MTX_B2],
    addr_pins=[board.MTX_ADDRA,
               board.MTX_ADDRB,
               board.MTX_ADDRC,
               board.MTX_ADDRD],
    clock_pin=board.MTX_CLK, latch_pin=board.MTX_LAT,
    output_enable_pin=board.MTX_OE)

# Associate matrix with a Display to use displayio features
DISPLAY = framebufferio.FramebufferDisplay(MATRIX, auto_refresh=False,
                                           rotation=0)

# Load BMP image, create Group and TileGrid to hold it
FILENAME = "wales.bmp"

# CircuitPython 6 & 7 compatible
BITMAP = displayio.OnDiskBitmap(open(FILENAME, "rb"))
TILEGRID = displayio.TileGrid(
    BITMAP,
    pixel_shader=getattr(BITMAP, 'pixel_shader', displayio.ColorConverter()),
    tile_width=BITMAP.width,
    tile_height=BITMAP.height
)

# # CircuitPython 7+ compatible
# BITMAP = displayio.OnDiskBitmap(FILENAME)
# TILEGRID = displayio.TileGrid(
#     BITMAP,
#     pixel_shader=BITMAP.pixel_shader,
#     tile_width=BITMAP.width,
#     tile_height=BITMAP.height
# )

GROUP = displayio.Group()
GROUP.append(TILEGRID)
DISPLAY.show(GROUP)
DISPLAY.refresh()

# Nothing interactive, just hold the image there
while True:
    pass

View on GitHub

Mfr Part # 3036
RGB MATRIX FEATHERWING KIT M0 M4
Adafruit Industries LLC
Mfr Part # 4702
ADAFRUIT RGB MATRIX FEATHERWING
Adafruit Industries LLC
Mfr Part # 2830
FEATHER STACKING HEADERS FML
Adafruit Industries LLC
Mfr Part # 2886
FEATHER HEADER KIT FML
Adafruit Industries LLC
Mfr Part # 2104
IDC BREAKOUT HELPER - 2X8 (16 PI
Adafruit Industries LLC
Mfr Part # 4775
METRO ESP32-S2
Adafruit Industries LLC
Mfr Part # 2601
RGB MATRIX SHIELD FOR ARDUINO
Adafruit Industries LLC
Add all DigiKey Parts to Cart
Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.