Libraries

We'll need to make sure we have these libraries installed. (Check out this link on installing libraries if needed.)

  • adafruit_bitmap_font
  • adafruit_bus_device
  • adafruit_display_text
  • adafruit_esp32spi
  • adafruit_io
  • adafruit_matrixportal
  • adafruit_requests.mpy
  • neopixel.mpy

Connect to the Internet

Once you have CircuitPython setup and libraries installed we can get your board connected to the Internet. The process for connecting can be found here.

Text Editor

Adafruit recommends using the Mu editor for editing your CircuitPython code. You can get more info in this guide.

Alternatively, you can use any text editor that saves simple text files.

Code

Click the Download: Project Zip File link below in the code window to get a zip file with all the files needed for the project. Copy code.py from the zip file and place it on the CIRCUITPY drive.

# Scoreboard matrix display
# uses AdafruitIO to set scores and team names for a scoreboard
# Perfect for cornhole, ping pong, and other games

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

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

RED_COLOR = 0xAA0000
BLUE_COLOR = 0x0000AA

# Red Score
matrixportal.add_text(
    text_font=terminalio.FONT,
    text_position=(4, int(matrixportal.graphics.display.height * 0.75) - 3),
    text_color=RED_COLOR,
    text_scale=2,
)

# Blue Score
matrixportal.add_text(
    text_font=terminalio.FONT,
    text_position=(36, int(matrixportal.graphics.display.height * 0.75) - 3),
    text_color=BLUE_COLOR,
    text_scale=2,
)

# Red Team name
matrixportal.add_text(
    text_font=terminalio.FONT,
    text_position=(4, int(matrixportal.graphics.display.height * 0.25) - 4),
    text_color=RED_COLOR,
)

# Blue Team name
matrixportal.add_text(
    text_font=terminalio.FONT,
    text_position=(36, int(matrixportal.graphics.display.height * 0.25) - 4),
    text_color=BLUE_COLOR,
)

# Static 'Connecting' Text
matrixportal.add_text(
    text_font=terminalio.FONT,
    text_position=(59, 0),
)

SCORES_RED_FEED = "scores-group.red-team-score-feed"
SCORES_BLUE_FEED = "scores-group.blue-team-score-feed"
TEAM_RED_FEED = "scores-group.red-team-name"
TEAM_BLUE_FEED = "scores-group.blue-team-name"
UPDATE_DELAY = 4

matrixportal.set_text_color(RED_COLOR, 0)
matrixportal.set_text_color(BLUE_COLOR, 1)


def show_connecting(show):
    if show:
        matrixportal.set_text(".", 4)
    else:
        matrixportal.set_text(" ", 4)


def get_last_data(feed_key):
    feed = matrixportal.get_io_feed(feed_key, detailed=True)
    value = feed["details"]["data"]["last"]
    if value is not None:
        return value["value"]
    return None


def customize_team_names():
    team_red = "Red"
    team_blue = "Blue"

    show_connecting(True)
    team_name = get_last_data(TEAM_RED_FEED)
    if team_name is not None:
        print("Team {} is now Team {}".format(team_red, team_name))
        team_red = team_name
    matrixportal.set_text(team_red, 2)
    team_name = get_last_data(TEAM_BLUE_FEED)
    if team_name is not None:
        print("Team {} is now Team {}".format(team_blue, team_name))
        team_blue = team_name
    matrixportal.set_text(team_blue, 3)
    show_connecting(False)


def update_scores():
    print("Updating data from Adafruit IO")
    show_connecting(True)

    score_red = get_last_data(SCORES_RED_FEED)
    if score_red is None:
        score_red = 0
    matrixportal.set_text(score_red, 0)

    score_blue = get_last_data(SCORES_BLUE_FEED)
    if score_blue is None:
        score_blue = 0
    matrixportal.set_text(score_blue, 1)
    show_connecting(False)


customize_team_names()
update_scores()
last_update = time.monotonic()

while True:
    # Set the red score text
    if time.monotonic() > last_update + UPDATE_DELAY:
        update_scores()
        last_update = time.monotonic()

Adafruit IO Setup

Our project will use Adafruit IO to serve up a feed of quotes and colors. Adafruit IO is absolutely free to use, but you'll need to log in with your Adafruit account to use it. If you don't already have an Adafruit login, create one here.

If you haven't used Adafruit IO before, check out this guide for more info.

Once you have logged into your account, there are two pieces of information you'll need to place in your secrets.py file: Adafruit IO username, and Adafruit IO key. Head to io.adafruit.com and simply click the View AIO Key link on the left hand side of the Adafruit IO page to get this information.

Then, add them to the secrets.py file like this:

secrets = {
    'ssid' : 'your_wifi_ssid',
    'password' : 'your_wifi_password',
    'aio_username' : '_your_aio_username_',
    'aio_key' : '_your_big_huge_super_long_aio_key_'
}

Problems Getting Score Data

If you have any problems getting the data to update correctly, check that the secrets.py file has the information noted above.

Adafruit IO Group, Feeds, Dashboard

Next, we'll create the necessary Adafruit IO Group, Feeds, and Dashboard to host our scores and team/player names.

First, if you're new to Adafruit IO, take a look at this excellent guide on getting started.

Next, we'll create a Group that will contain our four Feeds (two per player name, two per score).

Group Creation

In the Feeds screen click on the Actions menu and then pick Create a New Group from the dropdown menu.

Name the group Scores Group and then click Create.

Feed Creation

In the Feeds screen click on the Actions menu and then pick Create a New Feed from the dropdown menu.

Name this feed Blue Team Name, and select Scores Group from the Add to groups field, then click Create.

Repeat this process a second time to make a new Feed called Red Team Name.

Repeat this process two more times to create feeds named Blue Team Score Feed and Red Team Score Feed.

Dashboard Creation

Now that we have the four feeds we need, let's create a Dashboard with a couple of UI block elements that will make it easy to add data points to our feeds.

From the Dashboards page, click the Actions dropdown menu and select Create a New Dashboard.

Name the Dashboard as Scoreboard Dashboard. You can make a custom head image to display by checking the checkbox for Show Header Image and browsing to an image file -- you can use the one shown below if you like.

Then click Create.

Dashboard Widgets

Click on the create a new block + sign in the Dashboard page, this will present you with a number of UI element block options.

Pick the Text block.

From the Choose feed pop-up window that appears, chose the Blue Team Name feed, then click Next step.

In the Block settings popup window, give the block the title Text quote, then click Update block.

Repeat this process to create a Slider block with a Min value of 0 and a Max value of 21 (or whatever your game calls for), assigning it to the Blue Team Score Feed feed.

Repeat the two steps from above for the Red Team Name and Red Team Score Feed.

Score a Game

Time to score a game! You will adjust those values from any web browser -- including a mobile device browser. This puts a centralized score keeping remote right in your hands while playing or officiating a game.

Start by setting the two team names, and set the scores to 0-0.

Now, any time a player or team scores, move the slider. After a few moments, the Matrix Scoreboard will update to match!

Here, the Poughkeepsie Slugs have beaten the New Haven Orcs with a final score of 21-11!

Winner stays in the game, but a new contender emerges -- the East Marion Imps have made it all the way from Long Island for the game! In the Dashboard, we'll change the name from Orcs to Imps and press Return on the keyboard (or browser simulacrum thereof), then set the sliders to 0-0.

You can press the reset button on the Matrix Portal so it will grab the new team names (these are only set at startup to keep things fast).

We're ready to start scoring the new game! Go Slugs!!

How it Works

Here's a look at the code and how it functions.

First, we load in some libraries:

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

Next, the Display is set up using the matrixportal object. We also define the team colors and create text label objects for the team names and scores and static connecting text:

matrixportal = MatrixPortal(status_neopixel=board.NEOPIXEL, debug=False)

RED_COLOR = 0xAA0000
BLUE_COLOR = 0x0000AA

# Red Score
matrixportal.add_text(
    text_font=terminalio.FONT,
    text_position=(4, int(matrixportal.graphics.display.height * 0.75) - 3),
    text_color=RED_COLOR,
    text_scale=2,
)

# Blue Score
matrixportal.add_text(
    text_font=terminalio.FONT,
    text_position=(36, int(matrixportal.graphics.display.height * 0.75) - 3),
    text_color=BLUE_COLOR,
    text_scale=2,
)

# Red Team name
matrixportal.add_text(
    text_font=terminalio.FONT,
    text_position=(4, int(matrixportal.graphics.display.height * 0.25) - 4),
    text_color=RED_COLOR,
)

# Blue Team name
matrixportal.add_text(
    text_font=terminalio.FONT,
    text_position=(36, int(matrixportal.graphics.display.height * 0.25) - 4),
    text_color=BLUE_COLOR,
)

# Static 'Connecting' Text
matrixportal.add_text(
    text_font=terminalio.FONT,
    text_position=(59, 0),
)

Feed Variables

In order to use the feed data, we need a few  variables to help traverse the AIO json data, as well as set the update delay variable to 4 seconds.

We also set the text colors here.

SCORES_RED_FEED = "scores-group.red-team-score-feed"
SCORES_BLUE_FEED = "scores-group.blue-team-score-feed"
TEAM_RED_FEED = "scores-group.red-team-name"
TEAM_BLUE_FEED = "scores-group.blue-team-name"
UPDATE_DELAY = 4

matrixportal.set_text_color(RED_COLOR, 0)
matrixportal.set_text_color(BLUE_COLOR, 1)

Connecting Function

The show_connecting() function is used to display a dot symbol when the device is connecting to AIO over the WiFi access point.

def show_connecting(show):
    if show:
        matrixportal.set_text(".", 4)
    else:
        matrixportal.set_text(" ", 4)

Get Data Function

This function is used to pull in and parse the feed data from Adafruit IO.

def get_last_data(feed_key):
    feed = matrixportal.get_io_feed(feed_key, detailed=True)
    value = feed["details"]["data"]["last"]
    if value is not None:
        return value["value"]
    return None

Customize Team Names

This function pulls in the team names from the AIO feed.

def customize_team_names():
    team_red = "Red"
    team_blue = "Blue"

    show_connecting(True)
    team_name = get_last_data(TEAM_RED_FEED)
    if team_name is not None:
        print("Team {} is now Team {}".format(team_red, team_name))
        team_red = team_name
    matrixportal.set_text(team_red, 2)
    team_name = get_last_data(TEAM_BLUE_FEED)
    if team_name is not None:
        print("Team {} is now Team {}".format(team_blue, team_name))
        team_blue = team_name
    matrixportal.set_text(team_blue, 3)
    show_connecting(False)

Update Scores Function

This function is used to pull in the updated score data and then adjust the text to match.

def update_scores():
    print("Updating data from Adafruit IO")
    show_connecting(True)

    score_red = get_last_data(SCORES_RED_FEED)
    if score_red is None:
        score_red = 0
    matrixportal.set_text(score_red, 0)

    score_blue = get_last_data(SCORES_BLUE_FEED)
    if score_blue is None:
        score_blue = 0
    matrixportal.set_text(score_blue, 1)
    show_connecting(False)

The last steps of the setup are to run the customize_team_names() function and the update_scores() function, and then set a timekeeping variable.

customize_team_names()
update_scores()
last_update = time.monotonic()

Main Loop

The main loop of the program simply waits for the four second update delay to pass and then calls update_scores(), and resets the last_update time.

while True:
    # Set the red score text
    if time.monotonic() > last_update + UPDATE_DELAY:
        update_scores()
        last_update = time.monotonic()

This guide was first published on Oct 21, 2020. It was last updated on Oct 21, 2020.

This page (Code the Scoreboard) was last updated on Oct 23, 2021.

Text editor powered by tinymce.