Raspberry Pi Pico
11 x 7 LED Matrix - Web Messages
Having successfully connected to a web API in the Weather over Wifi project, I wanted to see if I could get messages from an API scrolling across one of the IS31FL3731 matrices that I had. The photograph here shows the circuit I made. I am using a Pimoroni Pico Breakout Garden Pack and their 11 x 7 LED matrix breakout. The breakout garden pack is on one of their Omnibus Dual Expanders with the other set of pins being used for a Pimoroni Pico Wireless Pack. Other than the Pico itself, the hardware is all from Pimoroni.
You can make out a few letters of the fact that was scrolling across the matrix - something about American's, I believe. The idea with the project was to scroll a new fact across the display once every minute or so.
The software side of this project is all about Adafruit's CircuitPython. You need the following libraries for this project. Download the mpy library for these and copy to your lib folder.
Adafruit have an excellent library for the IS31FL3731 and it does include a subclass for using the 11 x 7 LED matrix. I wanted to the ability to scroll text and, having already implemented that in MicroPython, thought it would be easier just to convert my library. It turned out to be very easy to do. The following program was saved as scroll1x7.py and placed in the lib folder on the Pico.
from time import sleep font = 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') _f = 0 _b = bytearray(145) class Matrix: def __init__(self, i2c): self.i2c = i2c while not self.i2c.try_lock(): pass self._w(253, 11) sleep(0.1) self._w(10, 0) sleep(0.1) self._w(10, 1) sleep(0.1) self._w(0, 0) self._w(6, 0) for bank in [1,0]: self._w(253, bank) self._w([0] + [255] * 17) self.clear() self.show() def _w(self, *args): if len(args) == 1: args = args[0] self.i2c.writeto(117, bytes(args)) def clear(self): global _b del _b _b = bytearray(145) def fill(self, v): global _b del _b _b = bytearray([v]*145) def show(self): global _f _f = not _f self._w(253, _f) _b[0] = 36 self._w(_b) self._w(253, 11) self._w(1, _f) def set_pixel(self,col, row, brightness): global _b _b[self._pixel_addr(col, row)] = brightness def get_pixel(self, col, row): global _b return _b[self._pixel_addr(col, row)] def _pixel_addr(self, x, y): return (x << 4) - y + 1 + (6 if x <= 5 else -82) def scroll(self, txt, delay, v): global _b msg = bytearray([0]*11) for c in txt: msg += bytearray(b'\x00') msg += font[(ord(c)-32)*5:(ord(c)-32)*5+5] msg += bytearray(b'\x00') msg += bytearray([0]*11) for i in range(len(msg)-11): m = msg[i:i+11] for y in range(7): for x in range(11): if m[x]>>y & 1: self.set_pixel(x,y,v) else: self.set_pixel(x,y,0) self.show() sleep(delay)
The next job is to make a secrets.py file with the login details for your wireless network or mobile phone hotspot.
# This file is where you keep secret settings, passwords, and tokens! # If you put them in the code you risk committing that info or sharing it secrets = { 'ssid' : "YOUR SSID", 'password' : 'YOUR PASSWORD', 'timezone' : "Europe/London", # http://worldtimeapi.org/timezones }
Here is the finished program. I have left in a variable with the URL for a cat facts API. If you swap that in, use 'fact' instead of text for the JSON. Both of these APIs do not require any authentication or a token of any sort.
import board import busio import adafruit_requests as requests import adafruit_esp32spi.adafruit_esp32spi_socket as socket from digitalio import DigitalInOut from adafruit_esp32spi import adafruit_esp32spi from scroll11x7 import Matrix from time import sleep i2c = busio.I2C(board.GP5, board.GP4) display = Matrix(i2c) # Get wifi details and more from a secrets.py file try: from secrets import secrets except ImportError: print("WiFi secrets are kept in secrets.py, please add them there!") raise esp32_cs = DigitalInOut(board.GP7) esp32_ready = DigitalInOut(board.GP10) esp32_reset = DigitalInOut(board.GP11) spi = busio.SPI(board.GP18, board.GP19, board.GP16) esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) requests.set_socket(socket, esp) if esp.status == adafruit_esp32spi.WL_IDLE_STATUS: print("ESP32 found and in idle mode") print("Firmware vers.", esp.firmware_version) print("MAC addr:", [hex(i) for i in esp.MAC_address]) for ap in esp.scan_networks(): print("\t%s\t\tRSSI: %d" % (str(ap["ssid"], "utf-8"), ap["rssi"])) print("Connecting to AP...") while not esp.is_connected: try: esp.connect_AP(secrets["ssid"], secrets["password"]) except RuntimeError as e: print("could not connect to AP, retrying: ", e) continue print("Connected to", str(esp.ssid, "utf-8"), "\tRSSI:", esp.rssi) cat_url = "https://catfact.ninja/fact" # fact fact_url = "https://uselessfacts.jsph.pl/random.json?language=en" # text while True: r = requests.get(fact_url) fact = r.json()["text"] print(fact) display.scroll(fact, 0.02, 128) sleep(60)
Watching the messages on the matrix is a little uncomfortable, even at low brightness. It is, however, a nice project to prove the concept. It also shows that my IS31FL3731 library can be repurposed quickly in CircuitPython whenever needed.