Metro Sign Build: Working with LED Display Libraries
Build Log 3: MatrixPortal.Matrix + display_manager
Hope you enjoyed getting your components together! If this seems like a project you want to take on, or you just want to see how mine goes, consider subscribing so you don’t miss a build log!
Working with LED Display Libraries: MatrixPortal.Matrix
Now that we have our MatrixPortal working, let’s get some text up on our LED panels! We’re going to be using two key libraries to draw text (and later icons) on the panels: adafruit_matrixportal.matrix
and displayio
. Displaying content on the panels using these libraries involves creating and displaying displayio
groups, which can nest in each other to form more complex displays1. We’re going to create a group for each of the text areas we want to create down the line, but for now we only need to create a root group as our largest group container, and a label group for our first text label. I followed this guide2 and chose to create a new display_manager.py
file to run all the displayio
functions. In addition to the displayio
library, you’re going to want a few more:
terminalio
: this drives the default font for textadafruit_display_text.label
: this creates the text labels needed to display text
Creating our display_manager class
Now we can create our new display_manager class and set up initialization:
class display_manager(displayio.Group): def __init__( self, display, ): super().__init__() self.display = display # set up label groups self.root_group = displayio.Group() self.root_group.append(self) self._label_group = displayio.Group(x=4, y=8) self.append(self._label_group)
We’re initializing the display, then creating two nested displayio.Groups
, our root_group
at the top level and our label_group
at the bottom level. We’re going to add our text labels to our label_group
, then display our root_group
. This structure allows us to continue adding different groups to our root_group and display them all at once. Note that we’ve passed in some optional parameters in our label_group
that will pin the group to specific x and y coordinates; this isn’t necessary now, but will be later for shifting our different label groups around each other!
Now we need to actually add our text label to our label_group
:
# set up text label
self.label_text = Label(terminalio.FONT)
self.label_text.color = 0xFFFFFF
self._label_group.append(self.label_text)
Here we’re spinning up our new label and setting the color to white in hexadecimal. We also have to add our label to our label_group
, and we should be good to go!
The other function we need to create is a way to actually update the text label we just created; let’s make a new function that intakes a string and displays that string. Note that displayio
doesn’t do any formatting, so long strings will just run off of the panel!
def update_label(self, input_text):
self.label_text.text = input_text
One more function you’ll want is a way to refresh the display; this is super simple:
# refresh the root group on the display
def refresh_display(self):
self.display.show(self.root_group)
Calling our new display_manager in code.py
NOTE: don’t forget to import our display_manager.py
file into our code.py
file!
Back in our code.py
file, we’ll pass in a Matrix display object as part of our display initialization. I’ll be honest, I was doing this in a very convoluted way on my first iteration, going deeper into the libraries than necessary; the MatrixPortal M4 tutorial covers the hierarchy well if you want to learn more about the different levels of display and network classes under the main MatrixPortal
library3. Notice the optional width
and height
parameters we pass into the matrix; this is what merges our two LED panels into one display.
matrix = Matrix(width=128, height=32, bit_depth=2, tile_rows=1)
display_manager = display_manager.display_manager(matrix.display)
Now that we have our display_manager
object, we can call the update_text
function and refresh the display to actually display text on our panels! Sharing the full code for both code.py
and display_manager.py
at the end of the post.
Conclusion
We’ve met our Hello World! criteria! In the next log, we’ll tackle our first API implementation so we can get some real Metro data on our panel. If you have any improvement suggestions, questions, or you can’t get the code to work, leave a comment or let me know on Twitter; appreciate your feedback!
Code Examples
code.py
import board | |
import busio | |
from digitalio import DigitalInOut, Pull | |
import neopixel | |
from adafruit_matrixportal.matrix import Matrix | |
from adafruit_esp32spi import adafruit_esp32spi | |
from adafruit_esp32spi import adafruit_esp32spi_wifimanager | |
import display_manager | |
# --- SECRETS SETUP ---- | |
try: | |
from secrets import secrets | |
print("secrets loaded") | |
except ImportError: | |
print("Wifi + constants are kept in secrets.py, please add them there!") | |
raise | |
# --- INITIALIZE DISPLAY ----------------------------------------------- | |
# MATRIX DISPLAY MANAGER | |
matrix = Matrix(width=128, height=32, bit_depth=2, tile_rows=1) | |
display_manager = display_manager.display_manager(matrix.display) | |
print("display manager loaded") | |
# --- WIFI SETUP ------------- | |
# Initialize ESP32 Pins: | |
esp32_cs = DigitalInOut(board.ESP_CS) | |
esp32_ready = DigitalInOut(board.ESP_BUSY) | |
esp32_reset = DigitalInOut(board.ESP_RESET) | |
# Initialize wifi components | |
spi = busio.SPI(board.SCK, board.MOSI, board.MISO) | |
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) | |
# Initialize neopixel status light | |
status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2) | |
# Initialize wifi object | |
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) | |
print("WiFi loaded") | |
while True: | |
display_manager.update_label("Hello World!") | |
display_manager.refresh_display() |
display_manager.py
import displayio | |
import terminalio | |
from adafruit_display_text.label import Label | |
from adafruit_bitmap_font import bitmap_font | |
# Initialize display_manager class | |
class display_manager(displayio.Group): | |
def __init__( | |
self, | |
display, | |
): | |
super().__init__() | |
self.display = display | |
# set up label groups | |
self.root_group = displayio.Group() | |
self.root_group.append(self) | |
self._label_group = displayio.Group(x=4, y=8) | |
self.append(self._label_group) | |
# set up text label | |
self.label_text = Label(terminalio.FONT) | |
self.label_text.color = 0xFFFFFF | |
self._label_group.append(self.label_text) | |
# update text in the text label | |
def update_label(self, input_text): | |
self.label_text.text = input_text | |
# refresh the root group on the display | |
def refresh_display(self): | |
self.display.show(self.root_group) |