Raspberry Pi Pico
Unicorn HAT

The Unicorn HAT is a Raspberry Pi accessory made by the UK electronics company, Pimoroni. It has 64 WS2812 (Neopixel RGB) LEDs arranged in an 8x8 grid.

In this photograph, I have the Unicorn HAT connected to the Pico using a Waveshare Pico to HAT board.

Pico Circuit

Since the LEDs are Neopixels, there is a CircuitPython library that makes it easy to drive the LEDs. On the Unicorn HAT, the pixels are connected down,up,down,... in columns, making it a little awkward when you want to work with grid coordinates. I added a subroutine to do that. The other thing I wanted to do was to have text scrolling across the matrix.

You need a library for this project - get the latest .mpy one from here,

I started by taking the font I have used in various projects and making a separate file for it. I saved this as font.py. I can add other fonts to this file and import them when I need them.

font7x5 = bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x5f\x00\x00\x00\x07\x00\x07\x00\x14\x7f\x14'
                b'\x7f\x14\x24\x2a\x7f\x2a\x12\x23\x13\x08\x64\x62\x36\x49\x55\x22\x50\x00'
                b'\x05\x03\x00\x00\x00\x1c\x22\x41\x00\x00\x41\x22\x1c\x00\x14\x08\x3e\x08'
                b'\x14\x08\x08\x3e\x08\x08\x00\x50\x30\x00\x00\x08\x08\x08\x08\x08\x00\x60'
                b'\x60\x00\x00\x20\x10\x08\x04\x02\x3e\x51\x49\x45\x3e\x00\x42\x7f\x40\x00'
                b'\x42\x61\x51\x49\x46\x21\x41\x45\x4b\x31\x18\x14\x12\x7f\x10\x27\x45\x45'
                b'\x45\x39\x3c\x4a\x49\x49\x30\x01\x71\x09\x05\x03\x36\x49\x49\x49\x36\x06'
                b'\x49\x49\x29\x1e\x00\x36\x36\x00\x00\x00\x56\x36\x00\x00\x08\x14\x22\x41'
                b'\x00\x14\x14\x14\x14\x14\x00\x41\x22\x14\x08\x02\x01\x51\x09\x06\x32\x49'
                b'\x79\x41\x3e\x7e\x11\x11\x11\x7e\x7f\x49\x49\x49\x36\x3e\x41\x41\x41\x22'
                b'\x7f\x41\x41\x22\x1c\x7f\x49\x49\x49\x41\x7f\x09\x09\x09\x01\x3e\x41\x49'
                b'\x49\x7a\x7f\x08\x08\x08\x7f\x00\x41\x7f\x41\x00\x20\x40\x41\x3f\x01\x7f'
                b'\x08\x14\x22\x41\x7f\x40\x40\x40\x40\x7f\x02\x0c\x02\x7f\x7f\x04\x08\x10'
                b'\x7f\x3e\x41\x41\x41\x3e\x7f\x09\x09\x09\x06\x3e\x41\x51\x21\x5e\x7f\x09'
                b'\x19\x29\x46\x46\x49\x49\x49\x31\x01\x01\x7f\x01\x01\x3f\x40\x40\x40\x3f'
                b'\x1f\x20\x40\x20\x1f\x3f\x40\x38\x40\x3f\x63\x14\x08\x14\x63\x07\x08\x70'
                b'\x08\x07\x61\x51\x49\x45\x43\x00\x7f\x41\x41\x00\x02\x04\x08\x10\x20\x00'
                b'\x41\x41\x7f\x00\x04\x02\x01\x02\x04\x40\x40\x40\x40\x40\x00\x01\x02\x04'
                b'\x00\x20\x54\x54\x54\x78\x7f\x48\x44\x44\x38\x38\x44\x44\x44\x20\x38\x44'
                b'\x44\x48\x7f\x38\x54\x54\x54\x18\x08\x7e\x09\x01\x02\x0c\x52\x52\x52\x3e'
                b'\x7f\x08\x04\x04\x78\x00\x44\x7d\x40\x00\x20\x40\x44\x3d\x00\x7f\x10\x28'
                b'\x44\x00\x00\x41\x7f\x40\x00\x7c\x04\x18\x04\x78\x7c\x08\x04\x04\x78\x38'
                b'\x44\x44\x44\x38\x7c\x14\x14\x14\x08\x08\x14\x14\x18\x7c\x7c\x08\x04\x04'
                b'\x08\x48\x54\x54\x54\x20\x04\x3f\x44\x40\x20\x3c\x40\x40\x20\x7c\x1c\x20'
                b'\x40\x20\x1c\x3c\x40\x30\x40\x3c\x44\x28\x10\x28\x44\x0c\x50\x50\x50\x3c'
                b'\x44\x64\x54\x4c\x44\x00\x08\x36\x41\x00\x00\x00\x7f\x00\x00\x00\x41\x36'
                b'\x08\x00\x10\x08\x08\x10\x08\x00\x00\x00\x00\x00')

Here is the program that I used to test things out.

from time import sleep
import board
import neopixel
from random import randint
from font import font7x5

def wax_on(rgb, delay):
    for i in range(num_pixels):
        pixels[i] = rgb
        pixels.show()
        sleep(delay)

def wax_off(rgb, delay):
    for i in range(num_pixels-1, -1, -1):
        pixels[i] = rgb
        pixels.show()
        sleep(delay)

def set_pixelxy(x, y, rgb):
    if x % 2 == 0:
        i = 8 * x + y
    else:
        i = x * 8 + 7 - y
    pixels[i] = rgb

def scroll(txt, delay, rgb):
    msg = bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00')
    for c in txt:
        msg += bytearray(b'\x00')
        msg += font7x5[(ord(c)-32)*5:(ord(c)-32)*5+5]
        msg += bytearray(b'\x00')
    msg += bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00')
    for i in range(len(msg)- 8):
        t = msg[i:i + 8]
        for x in range(8):
            for y in range(8):
                if t[x] >> y & 1:
                    set_pixelxy(x, y, rgb)
                else:
                    set_pixelxy(x, y, off)
        pixels.show()
        sleep(delay)

pixel_pin = board.GP18
num_pixels = 64
ORDER = neopixel.GRB
pixels = neopixel.NeoPixel(pixel_pin, num_pixels, brightness=0.2, auto_write=False, pixel_order=ORDER)

off = (0, 0, 0)

pixels.fill(off)
pixels.show()

for y in range(8):
    for x in range(8):
        set_pixelxy(x, y, (255, 0, 0))
        pixels.show()
        sleep(0.1)

sleep(3)
pixels.fill(off)
pixels.show()
scroll("OMG. I can't believe that the scrolling works.", 0.05, (0,0,255))
sleep(3)
while True:
    colour = (randint(0,255),randint(0,255), randint(0,255))
    wax_on(colour, 0.1)
    sleep(0.5)
    wax_off(off, 0.1)
    sleep(0.5)