Raspberry Pi Pico
LED Dot Matrix Breakout

The LED Dot Matrix Breakout is another one of those cute little boards from Pimoroni's breakout garden range. The board has a pair of 5x7 LED dot matrices - you can have green or red. It also has a driver chip. It uses the IS31FL3730 LED driver. This is the same chip that was used in the original Scroll pHAT. It costs £12.90 at the time of writing this page.

This photograph shows the board and another couple of high quality Pimoroni products. The matrix breakout is slotted into the Pico Breakout Garden Pack. This has two slots for I2C breakouts and one for SPI. It can be connected directly to the Pico like other Pimoroni packs or, as you see here, you can pop it onto a Pico Omnibus (Dual Expander) which has headers for connecting the Pico and two packs.

Pico Circuit

As with the Scroll Pack, the 11x7 LED Matrix and the 5x5 RGB matrix, you can save yourself the trouble of copying or working with this library by downloading Pimoroni's custom version of MicroPython which has support for a massive range of their products already built in. I wanted to have a go at this myself and leave the option for more customisation later on.

For the circuit, the Pico Omnibus is doing the connections for me. 3V3 and GND are connected as you would expect. The Omnibus connects SDA to GP4 and SCL to GP5.

This section from the Pimoroni Python library for Rapsberry Pi shows how the matrices are connected to the driver.

    """
    _buf_matrix_left = [
    # Row  7654321
        0b01111111,  # col 1, bottom = msb
        0b01111111,  # col 2
        0b01111111,  # col 3
        0b01111111,  # col 4
        0b01111111,  # col 5
        0b00000000,
        0b00000000,
        0b01000000   # bit 7 = decimal dot
    ]
    _buf_matrix_right = [
    # Col    12345
        0b00011111,  # row 1
        0b00011111,  # row 2
        0b00011111,  # row 3
        0b00011111,  # row 4
        0b00011111,  # row 5
        0b00011111,  # row 6
        0b10011111,  # row 7 + bit 8 = decimal dot
        0b00000000
    ]
    """

They are connected as 2 matrices. The registers for the matrix data for the right hand matrix start at 0x01. For the left, they start at 0x0E. This requires a little extra programming but is a price worth paying for getting this onto such a small breakout.

This is the library I put together. Save it to the lib folder as dotmatrix.py. Depending on how you are using the breakout, you can delete parts of the library that you don't need.

from micropython import const
from time import sleep_ms

_ADDRESS = const(0x61)

fnt = [[0, 0, 0, 0, 0], [0, 0, 95, 0, 0], [0, 7, 0, 7, 0], [20, 127, 20, 127, 20], [36, 42, 127, 42, 18],
       [35, 19, 8, 100, 98], [54, 73, 85, 34, 80], [0, 5, 3, 0, 0], [0, 28, 34, 65, 0], [0, 65, 34, 28, 0],
       [20, 8, 62, 8, 20], [8, 8, 62, 8, 8], [0, 80, 48, 0, 0], [8, 8, 8, 8, 8], [0, 96, 96, 0, 0],
       [32, 16, 8, 4, 2], [62, 81, 73, 69, 62], [0, 66, 127, 64, 0], [66, 97, 81, 73, 70], [33, 65, 69, 75, 49],
       [24, 20, 18, 127, 16], [39, 69, 69, 69, 57], [60, 74, 73, 73, 48], [1, 113, 9, 5, 3], [54, 73, 73, 73, 54],
       [6, 73, 73, 41, 30], [0, 54, 54, 0, 0], [0, 86, 54, 0, 0], [8, 20, 34, 65, 0], [20, 20, 20, 20, 20],
       [0, 65, 34, 20, 8], [2, 1, 81, 9, 6], [50, 73, 121, 65, 62], [126, 17, 17, 17, 126], [127, 73, 73, 73, 54],
       [62, 65, 65, 65, 34], [127, 65, 65, 34, 28], [127, 73, 73, 73, 65], [127, 9, 9, 9, 1],
       [62, 65, 73, 73, 122], [127, 8, 8, 8, 127], [0, 65, 127, 65, 0], [32, 64, 65, 63, 1], [127, 8, 20, 34, 65],
       [127, 64, 64, 64, 64], [127, 2, 12, 2, 127], [127, 4, 8, 16, 127], [62, 65, 65, 65, 62], [127, 9, 9, 9, 6],
       [62, 65, 81, 33, 94], [127, 9, 25, 41, 70], [70, 73, 73, 73, 49], [1, 1, 127, 1, 1], [63, 64, 64, 64, 63],
       [31, 32, 64, 32, 31], [63, 64, 56, 64, 63], [99, 20, 8, 20, 99], [7, 8, 112, 8, 7], [97, 81, 73, 69, 67],
       [0, 127, 65, 65, 0], [2, 4, 8, 16, 32], [0, 65, 65, 127, 0], [4, 2, 1, 2, 4], [64, 64, 64, 64, 64],
       [0, 1, 2, 4, 0], [32, 84, 84, 84, 120], [127, 72, 68, 68, 56], [56, 68, 68, 68, 32], [56, 68, 68, 72, 127],
       [56, 84, 84, 84, 24], [8, 126, 9, 1, 2], [12, 82, 82, 82, 62], [127, 8, 4, 4, 120], [0, 68, 125, 64, 0],
       [32, 64, 68, 61, 0], [127, 16, 40, 68, 0], [0, 65, 127, 64, 0], [124, 4, 24, 4, 120], [124, 8, 4, 4, 120],
       [56, 68, 68, 68, 56], [124, 20, 20, 20, 8], [8, 20, 20, 24, 124], [124, 8, 4, 4, 8], [72, 84, 84, 84, 32],
       [4, 63, 68, 64, 32], [60, 64, 64, 32, 124], [28, 32, 64, 32, 28], [60, 64, 48, 64, 60],
       [68, 40, 16, 40, 68], [12, 80, 80, 80, 60], [68, 100, 84, 76, 68], [0, 8, 54, 65, 0], [0, 0, 127, 0, 0],
       [0, 65, 54, 8, 0], [16, 8, 8, 16, 8], [0, 0, 0, 0, 0]]

class Dotmatrix:
    def __init__(self, i2c):
        self.i2c = i2c
        self._buf_left = bytearray(8)
        self._buf_right = bytearray(8)        
        self.set_brightness(127)
        self.show()
    
    def set_brightness(self, b):
        b = min(127, max(0, b))
        self.i2c.writeto_mem(_ADDRESS, 0x19, bytes([b]))
    
    def show(self):
        self.i2c.writeto_mem(_ADDRESS, 0x0E, self._buf_left)
        self.i2c.writeto_mem(_ADDRESS, 0x01, self._buf_right)
        self.i2c.writeto_mem(_ADDRESS, 0x0, b'\x18')
        self.i2c.writeto_mem(_ADDRESS, 0x0D, b'\x0E')
        self.i2c.writeto_mem(_ADDRESS, 0x0C, b'\x01')
    
    def clear(self):
        self._buf_left = bytearray(8)
        self._buf_right = bytearray(8)
    
    def set_pixel(self, x, y, c):
        if x < 5:
            if c:
                self._buf_left[x] |= (0b1 << y)
            else:
                self._buf_left[x] &= ~(0b1 << y)
        else:            
            x -= 5
            if c:
                self._buf_right[y] |= (0b1 << x)
            else:
                self._buf_right[y] &= ~(0b1 << x)
    
    def set_decimal(self, left = 0, right = 0):
        if left:
            self._buf_left[7]  |= 0b01000000
        else:
            self._buf_left[7]  &= 0b10111111
        if right:
            self._buf_right[6] |= 0b10000000
        else:
            self._buf_right[6] &= 0b01111111
    
    # show a single character at position 0 or 1
    def show_char(self, c, position):
        c = ord(c) - 32
        if c<0 or c>len(fnt):
            return
        txt = fnt[c]
        position *= 5
        for y in range(7):
            for x in range(5):
                self.set_pixel(x+position, y, txt[x] >> y & 1)

    # display an integer from 0 to 99
    def show_num(self, n):
        if n<0 or n>99:
            return
        self.show_char(str(n // 10), 0)
        self.show_char(str(n % 10), 1)
        
    def scroll_message(self, message, delay):
        data = [0] * 10
        for c in message:
            data += fnt[(ord(c)-32)]
            data += [0]
        data += [0] * 10
        for i in range(len(data)-10):
            m = data[i:i+10]
            for y in range(7):
                for x in range(10):
                    self.set_pixel(x, y, m[x] >> y & 1)
            self.show()
            sleep_ms(delay)

Here is my test code to show how all those methods work.

from machine import Pin, I2C
from dotmatrix import Dotmatrix
from time import sleep

i2c=I2C(0,sda=Pin(4), scl=Pin(5))

display = Dotmatrix(i2c)

# pixels, one at a time
for y in range(7):
    for x in range(10):
        display.set_pixel(x,y,1)
        display.show()
        sleep(0.1)
sleep(1)

# change brightness
for b in range(127,-1,-1):
    display.set_brightness(b)
    display.show()
    sleep(0.05)
sleep(1)

display.clear()
display.show()
display.set_brightness(127)


# decimal points
display.set_decimal(1, 1)
display.show()
sleep(3)

display.clear()
display.show()
sleep(1)

# displaying integers 0 - 99
for i in range(100):
    display.show_num(i)
    display.show()
    sleep(0.1)
sleep(1)

display.clear()
display.show()

# scrolling text
display.scroll_message("I hope that this works.", 50)

Conclusions

This is a nice board. The dots make for a really clear display. You can configure the I2C address by cutting a trace on the back of the board. That would give you a few extra digits for a clock, timer, stopwatch or just something to show larger numbers.

There are few things I didn't get round to doing that would be fun to add to the library. Adding a custom character feature would allow special characters that are not in the font to be shown. You could also add a 'bitmap' feature.