Raspberry Pi Pico
Original Scroll pHAT

The orginal Scroll pHAT (not the HD one) was based on a completely different LED driver. It used the IS31FL3730. This has more basic variation of LED brightness and has fewer pixels. The trade-off is that it is a little simpler to drive. I had previously got this working on a BBC micro:bit and decided to have a quick go at making this work for the Pico. These don't seem to be on sale in the Pimoroni store but I do keep seeing them elsewhere.

Pico Circuit

My library code is here. I saved this as scrollphat.py. I went to some lengths to make a very compact font. It doesn't look amazing but, if you read the micro:bit page on this board, you can find out what I did.

from micropython import const
from time import sleep_ms

_ADDRESS = const(0x60)

font = [0x0,0x17,0xc03,0x2bea,0x6a76,0x2889,0x6aaa,0x3,0x45c0,0x1d1,0xc6,
        0x11c4,0x38c,0x1084,0x10,0xc98,0x7e3f,0x3e2,0x5ebd,0x7eb5,0x790f,
        0x76b7,0x76bf,0x7c21,0x7ebf,0x7eb7,0x140,0x340,0x4544,0x294a,0x1151,
        0x1ea0,0x5ebf,0x7cbf,0x6ebf,0x463f,0x3a3f,0x56bf,0x4bf,0x763f,0x7c9f,
        0x47f1,0x3e08,0x6c9f,0x421f,0x7cdf,0x709c,0x7e3f,0x1cbf,0x7b2e,0x6cbf,
        0x76b7,0x7e1,0x7e1f,0x3e0f,0x7d9f,0x6c9b,0x7e97,0x4eb9,0x47e0,0x6083,
        0x3f1,0x1826,0x4210,0x821,0x7298,0x729f,0x529c,0x7e9c,0x534c,0x17c4,
        0x7ab7,0x709f,0x3a0,0x3608,0x699f,0x3e0,0x6198,0x6098,0x7a5e,0x1cbf,
        0x7ca7,0x109e,0x26b2,0xbe2,0x721c,0x320c,0x330c,0x5114,0x1b16,0x4384]

class ScrollP:
    def __init__(self, i2c):
        self.i2c = i2c
        self.buffer = bytearray([0] * 11)
        self.offset = 0
        # set 5x11 mode
        self.i2c.writeto_mem(_ADDRESS, 0, b'\x03')
        self.set_brightness(32)
        self.show()
    
    def set_brightness(self, b):
        self.i2c.writeto_mem(_ADDRESS, 0x19, bytes([b]))
    
    def show(self):
        data = bytearray([0x01]) + self.buffer + bytearray([0xff])
        self.i2c.writeto(_ADDRESS, data)
        
    def set_pixel(self, x, y, value):
        if value:
            self.buffer[x] |= (1 << y)
        else:
            self.buffer[x] &= ~(1 << y)
    
    def get_char_bytes(self,c):
        if ord(c)<32 or ord(c)>122:
            return [0,0,0]
        else:
            return bytes([font[ord(c)-32]>>(i*5) & 31 for i in range(3)])

    def scroll_msg(self, msg, delay):
        data = bytearray([0]*11)
        for c in msg:
            data += bytearray(b'\x00')
            data += self.get_char_bytes(c)
            data += bytearray(b'\x00')
        data += bytearray([0]*11)
        for i in range(len(data)-11):
            self.buffer = data[i:i+11]
            self.show()
            sleep_ms(delay)

Here was some test code,

from machine import Pin, I2C
from scrollphat import ScrollP
from time import sleep

i2c=I2C(1,sda=Pin(2), scl=Pin(3))

display = ScrollP(i2c)

#test pixels
for y in range(5):
    for x in range(11):
        display.set_pixel(x,y,1)
        display.show()
        sleep(0.1)

# test scrolling
display.scroll_msg("OMG! The scrolling works.", 100)