In this page we'll go over the code from one of the many existing projects and take a deeper look at how the projects are making use of the library. The example that we want to take a look at comes from the Custom Scrolling Quote Board Matrix Display guide. This makes use of the top level matrixportal layer and makes use of several of the key features including mixing static text with scrolling text as well as use of Adafruit IO.

The script starts with importing the standard modules time and random because those are needed for this specific project. It imports board because the onboard NeoPixel is passed into the library to indicate the network connection status. Then the script imports terminalio because the built-in terminal font will be used for displaying the quotes. Finally, we import the MatrixPortal class from the matrixportal layer.

import time
import random
import board
import terminalio
from adafruit_matrixportal.matrixportal import MatrixPortal

Next it instantiates the MatrixPortal class as an object which is stored in the matrixportal variable. The board.NEOPIXEL is passed in as the status_neopixel parameter and debug is set to True in order to see extra messages, but this can also be set to False.

# --- Display setup ---
matrixportal = MatrixPortal(status_neopixel=board.NEOPIXEL, debug=True)

Next, the text labels are created. There are two different types that are created here. The first one is a scrolling text label and this will scroll in from the right side. This is used for the quotes themselves and is an ideal type when the text is too long to display all at once. One thing to note is that even though the text_position is given as an X and Y tuple, the X value is ignored for scrolling text labels and the Y is the position of the center of the label.

An important thing to consider with scrolling labels is that they cannot scroll while network data is being fetched because retrieving data is a blocking operation.

The second label is a static type label, which is used to display the Connecting message. The main reason we are displaying the Connecting message with a static type is because of the blocking issue. We are displaying it at a position of 2 for the X value and it is automatically being centered to the height of the display for the Y value. Just like the first type of label, this label uses the terminal.FONT type of font.

The first label that is created has an ID of 0 and each subsequent label is incremented. In the case of these 2 labels, the quotes label is label 0 and the connecting label is label 1. This will become important further down.

# Create a new label with the color and text selected
matrixportal.add_text(
    text_font=terminalio.FONT,
    text_position=(0, (matrixportal.graphics.display.height // 2) - 1),
    scrolling=True,
)

# Static 'Connecting' Text
matrixportal.add_text(
    text_font=terminalio.FONT,
    text_position=(2, (matrixportal.graphics.display.height // 2) - 1),
)

The next set of variables are a few variables that are intended to be changed to the your preferences. In this case, QUOTES_FEED and COLORS_FEED are the names of a couple of feeds that are designed to changed to match whatever you set up in Adafruit IO. The SCROLL_DELAY refers to the amount of time to wait between each scrolling animation frame. The smaller the number, the faster the text will scroll. Finally, the UPDATE_DELAY is the number of seconds to wait between checking for any changes from Adafruit IO.

QUOTES_FEED = "sign-quotes.signtext"
COLORS_FEED = "sign-quotes.signcolor"
SCROLL_DELAY = 0.02
UPDATE_DELAY = 600

Next the script sets up some initial values that will be changed once data is downloaded from Adafruit IO.

quotes = []
colors = []
last_color = None
last_quote = None

Next is the update_data() function. It starts out by defining the function, printing to the console that it is updating. Then it displays the Connecting message by setting the text of the label with an ID of 1 to "Connecting".

def update_data():
    print("Updating data from Adafruit IO")
    matrixportal.set_text("Connecting", 1)

Here we continue with the next part of the update_data() function. The code in both of these try blocks is nearly identical except for the fact that they grab the data from two different feeds. It makes use of the matrixportal.get_io_data() function to retrieve ALL values from a feed. This is useful if you have a small amount of data in your feed, but each piece of data counts against your data points that your IO account is allowed per minute, so if you are downloading a lot of data at once, you may want to consider this in your design.

The list that the data is to be stored in is cleared. After that, the data which was grabbed in the form of JSON data for each value, is iterated through a for loop as json_data. JSON data is laid out in a hierarchical tree very similar to the way that Python lists and  dictionaries can be laid out. The matrixportal.network.json_traverse() function is used to easily traverse down this hierarchical structure and grab the value from the JSON data by providing a path in the form of a list or tuple. That value is appended to the list.

You may have noticed that this last function call was accessing the network object inside of matrixportal. You can access ALL of the network layer functions through this object.

The reason that these are in a type/except block is because if there are any issues, the script can let you know what the issue is and continue on its way.

try:
    quotes_data = matrixportal.get_io_data(QUOTES_FEED)
    quotes.clear()
    for json_data in quotes_data:
        quotes.append(matrixportal.network.json_traverse(json_data, ["value"]))
    print(quotes)
# pylint: disable=broad-except
except Exception as error:
    print(error)

try:
    color_data = matrixportal.get_io_data(COLORS_FEED)
    colors.clear()
    for json_data in color_data:
        colors.append(matrixportal.network.json_traverse(json_data, ["value"]))
    print(colors)
# pylint: disable=broad-except
except Exception as error:
    print(error)

In the final part of the update_data() function it verifies that it was able to retrieve at least 1 value from each feed. If it was not able to, there's no point in continuing so an error is raised. The connecting message is hidden by setting the label with an ID of 1 to " ", though it could also be set to a completely empty string.

if not quotes or not colors:
        raise "Please add at least one quote and color to your feeds"
    matrixportal.set_text(" ", 1)

The next part is the final setup right before the main loop. It calls the update_data() function, which we went over above to retrieve the data first. It sets the last_update to the current timer value and makes sure "Connecting" isn't being displayed. The quote_index and color_index variables are set to ensure that there is no possibility that the variables could be read before they are assigned new values.

update_data()
last_update = time.monotonic()
matrixportal.set_text(" ", 1)
quote_index = None
color_index = None

Next we get to the main loop.

while True:

The remainder of the code is inside the main loop. The length of quotes is checked and only if we have more than one quote and have already set the quote, we run inside a loop to randomly choose a new index that is different from the last one we grabbed. If there is only one quote or we don't have an old value, there's no reason to run this while loop and we just grab one at random. We then set the last_quote to the new value we just grabbed which will be used next time.

# Choose a random quote from quotes
if len(quotes) > 1 and last_quote is not None:
    while quote_index == last_quote:
        quote_index = random.randrange(0, len(quotes))
else:
    quote_index = random.randrange(0, len(quotes))
last_quote = quote_index

In the next block, we do the exact same thing except with the colors.

# Choose a random color from colors
if len(colors) > 1 and last_color is not None:
    while color_index == last_color:
        color_index = random.randrange(0, len(colors))
else:
    color_index = random.randrange(0, len(colors))
last_color = color_index

After the random indices are chosen, the script sets the text and the color of the quote label. Since the label has an ID of 0, the index doesn't need to be specified.

# Set the quote text
matrixportal.set_text(quotes[quote_index])

# Set the text color
matrixportal.set_text_color(colors[color_index])

The matrixportal.scroll_text() function is called with the scroll delay parameter. This function will scroll the label from offscreen on the right all the way until it is offscreen on the left.

# Scroll it
matrixportal.scroll_text(SCROLL_DELAY)

Lastly, the amount of elapsed time on the built-in timer is checked against the UPDATE_DELAY value that was set near the top. If that amount of time has passed, which in this case is 600 seconds or 10 minutes, the data is updated and the last_update variable is set to the current value of the timer.

That's it. It keeps on looping from there.

if time.monotonic() > last_update + UPDATE_DELAY:
    update_data()
    last_update = time.monotonic()

Full Example Code

As reference, here's the full example code.

# SPDX-FileCopyrightText: 2020 John Park for Adafruit Industries
#
# SPDX-License-Identifier: MIT

# Quote board matrix display
# uses AdafruitIO to serve up a quote text feed and color feed
# random quotes are displayed, updates periodically to look for new quotes
# avoids repeating the same quote twice in a row

import time
import random
import board
import terminalio
from adafruit_matrixportal.matrixportal import MatrixPortal

# --- Display setup ---
matrixportal = MatrixPortal(status_neopixel=board.NEOPIXEL, debug=True)

# Create a new label with the color and text selected
matrixportal.add_text(
    text_font=terminalio.FONT,
    text_position=(0, (matrixportal.graphics.display.height // 2) - 1),
    scrolling=True,
)

# Static 'Connecting' Text
matrixportal.add_text(
    text_font=terminalio.FONT,
    text_position=(2, (matrixportal.graphics.display.height // 2) - 1),
)

QUOTES_FEED = "sign-quotes.signtext"
COLORS_FEED = "sign-quotes.signcolor"
SCROLL_DELAY = 0.02
UPDATE_DELAY = 600

quotes = []
colors = []
last_color = None
last_quote = None


def update_data():
    print("Updating data from Adafruit IO")
    matrixportal.set_text("Connecting", 1)

    try:
        quotes_data = matrixportal.get_io_data(QUOTES_FEED)
        quotes.clear()
        for json_data in quotes_data:
            quotes.append(matrixportal.network.json_traverse(json_data, ["value"]))
        print(quotes)
    # pylint: disable=broad-except
    except Exception as error:
        print(error)

    try:
        color_data = matrixportal.get_io_data(COLORS_FEED)
        colors.clear()
        for json_data in color_data:
            colors.append(matrixportal.network.json_traverse(json_data, ["value"]))
        print(colors)
    # pylint: disable=broad-except
    except Exception as error:
        print(error)

    if not quotes or not colors:
        raise RuntimeError("Please add at least one quote and color to your feeds")
    matrixportal.set_text(" ", 1)


update_data()
last_update = time.monotonic()
matrixportal.set_text(" ", 1)
quote_index = None
color_index = None

while True:
    # Choose a random quote from quotes
    if len(quotes) > 1 and last_quote is not None:
        while quote_index == last_quote:
            quote_index = random.randrange(0, len(quotes))
    else:
        quote_index = random.randrange(0, len(quotes))
    last_quote = quote_index

    # Choose a random color from colors
    if len(colors) > 1 and last_color is not None:
        while color_index == last_color:
            color_index = random.randrange(0, len(colors))
    else:
        color_index = random.randrange(0, len(colors))
    last_color = color_index

    # Set the quote text
    matrixportal.set_text(quotes[quote_index])

    # Set the text color
    matrixportal.set_text_color(colors[color_index])

    # Scroll it
    matrixportal.scroll_text(SCROLL_DELAY)

    if time.monotonic() > last_update + UPDATE_DELAY:
        update_data()
        last_update = time.monotonic()

This guide was first published on Oct 20, 2020. It was last updated on Mar 28, 2024.

This page (Code Example) was last updated on Mar 28, 2024.

Text editor powered by tinymce.