# LED Matrix Sports Scoreboard

## Overview

![](https://cdn-learn.adafruit.com/assets/assets/000/125/027/medium800/led_matrices_edited_P1410516.jpg?1696862947)

You can build a large RGB LED matrix display to monitor your favorite sport teams. A Matrix Portal S3 running CircuitPython requests data from the ESPN API to show your teams' gameplay data alongside team logos that are resized and gamma corrected to look crisp and bright on the matrices. If you've ever wanted to see all of your sportsball stats in one spot, this project is for you.&nbsp;

https://www.youtube.com/watch?v=jj0OQw5Ci9A

## Hardware and Power Requirements

The ESPN API generates a JSON response that is _huge_. It also takes a lot of processing power to interface with not one, not two but four 64x32 RGB LED matrices. Luckily the ESP32-S3 on the Matrix Portal S3 is able to handle the JSON and the matrices with its 8MB of flash and 2MB of SRAM. Previously this project would not have been possible with less powerful chips, like the SAMD51 on the original Matrix Portal. **TL;DR: make sure you are using a Matrix Portal S3 for this project**.

Warning: 

On top of processing power, four RGB LED matrices require a good power supply to ensure top pixel performance. In working on this project, the **best results were seen using two 5V 4A power supplies** : one for the two top panels and one for the two bottom panels. In this scenario, the Matrix Portal S3 is powered via its USB-C port, separately from the matrices.

Warning: 

## Parts
### Adafruit Matrix Portal S3 CircuitPython Powered Internet Display

[Adafruit Matrix Portal S3 CircuitPython Powered Internet Display](https://www.adafruit.com/product/5778)
Folks love our [wide selection of RGB matrices](https://www.adafruit.com/category/327) and accessories&nbsp;for making custom colorful LED displays... and our RGB Matrix Shields and FeatherWings can be quickly soldered together to make the wiring much easier. But what if we made it...

Out of Stock
[Buy Now](https://www.adafruit.com/product/5778)
[Related Guides to the Product](https://learn.adafruit.com/products/5778/guides)
![Video of Adafruit Matrix Portal S3 linked up to a matrix displaying the "Adafruit Matrix Portal" in white letters and red, green and blue circles jumping around. ](https://cdn-shop.adafruit.com/product-videos/640x480/5778-06.jpg)

### Pink and Purple Woven USB A to USB C Cable - 1 meter long

[Pink and Purple Woven USB A to USB C Cable - 1 meter long](https://www.adafruit.com/product/5153)
This cable is not only super-fashionable, with a woven pink and purple Blinka-like pattern, it's also made for USB C for our modernized breakout boards, Feathers, and more.&nbsp;&nbsp;[If you want something just like it but for Micro B, we...](https://www.adafruit.com/product/4111)

Out of Stock
[Buy Now](https://www.adafruit.com/product/5153)
[Related Guides to the Product](https://learn.adafruit.com/products/5153/guides)
![Angled shot of coiled pink and purple USB cable with USB A and USB C connectors.](https://cdn-shop.adafruit.com/640x480/5153-02.jpg)

### Four 64x32 RGB LED Matrices:
### 64x32 RGB LED Matrix - 4mm pitch

[64x32 RGB LED Matrix - 4mm pitch](https://www.adafruit.com/product/2278)
Bring a little bit of Times Square into your home with this sweet 64 x 32 square RGB LED matrix panel. These panels are normally used to make video walls, here in New York we see them on the sides of busses and bus stops, to display animations or short video clips. We thought they looked...

In Stock
[Buy Now](https://www.adafruit.com/product/2278)
[Related Guides to the Product](https://learn.adafruit.com/products/2278/guides)
![Two white hands hold out an assembled and powered on 64x32 RGB LED Matrix Panel - 4mm pitch. The matrix displays "Adafruit Industries LED MATRIX! 32x64 *RGB*"](https://cdn-shop.adafruit.com/640x480/2278-00.jpg)

### Four Acrylic Panels:
### Black LED Diffusion Acrylic Panel - 10.2" x 5.1"

[Black LED Diffusion Acrylic Panel - 10.2" x 5.1"](https://www.adafruit.com/product/4749)
&nbsp;nice whoppin' rectangular slab of some lovely black acrylic to add some extra diffusion to your LED Matrix project. This material is 2.6mm (0.1") thick and is made of special cast acrylic that makes it perfect for glowy projects, especially matrices or NeoPixels.

Unlike...

In Stock
[Buy Now](https://www.adafruit.com/product/4749)
[Related Guides to the Product](https://learn.adafruit.com/products/4749/guides)
![LED RGB matrix 10.2" x 5.1" with "Adafruit Industries LED Matrix" text showing, and LED acrylic slowly covering to make it nicely diffused](https://cdn-shop.adafruit.com/product-videos/640x480/4749-02.jpg)

### Two 5V 4A Power Supplies:
### 5V 4A (4000mA) switching power supply - UL Listed

[5V 4A (4000mA) switching power supply - UL Listed](https://www.adafruit.com/product/1466)
Need a lot of 5V power? This switching supply gives a clean regulated 5V output at up to **4 Amps** (4000mA). 110 or 240 input, so it works in any country. The plugs are "US 2-prong" style so you may need a plug adapter, but you can pick one up at any hardware store for $1 or so,...

Out of Stock
[Buy Now](https://www.adafruit.com/product/1466)
[Related Guides to the Product](https://learn.adafruit.com/products/1466/guides)
![Angled shot of 5V 4A switching power supply brick with power cable.](https://cdn-shop.adafruit.com/640x480/1466-10.jpg)

### Two Female DC Jack to Screw Terminal Block Adapters:
### Female DC Power adapter - 2.1mm jack to screw terminal block

[Female DC Power adapter - 2.1mm jack to screw terminal block](https://www.adafruit.com/product/368)
If you need to connect a DC power wall wart to a board that doesn't have a DC jack - this adapter will come in very handy! There is a 2.1mm DC jack on one end, and a screw terminal block on the other. The terminals are labeled with positive/negative assuming a positive-tip configuration...

In Stock
[Buy Now](https://www.adafruit.com/product/368)
[Related Guides to the Product](https://learn.adafruit.com/products/368/guides)
![Angle shot Female DC Power adapter - 2.1mm jack to screw terminal block](https://cdn-shop.adafruit.com/640x480/368-03.jpg)

### Part: Clear Adhesive Squares
quantity: 3
6 pack
[Clear Adhesive Squares](https://www.adafruit.com/product/4813)

### Part: M3 Screws
quantity: 1
M3 thread
[M3 Screws](https://www.adafruit.com/product/4685)

# LED Matrix Sports Scoreboard

## 3D Printing

![](https://cdn-learn.adafruit.com/assets/assets/000/124/411/medium800/led_matrices_edited_P1410429.jpg?1694713812)

You can 3D print brackets to hold the matrices together. Note that these have been designed to fit the [4mm pitch matrices](https://www.adafruit.com/product/2278). You'll print one center bracket and six of the smaller 1x2 brackets. The parts can be downloaded from Printables or directly below.

[Printables Download](https://www.printables.com/model/582588-brackets-for-rgb-led-matrices-4mm-pitch)
[RGB_LED_Matrix_Brackets_4mm_Pitch.zip](https://cdn-learn.adafruit.com/assets/assets/000/124/414/original/RGB_LED_Matrix_Brackets_4mm_Pitch.zip?1694714174)
Info: 

A plus sign shaped bracket fits over the intersection in the middle of the four matrices. It is secured with four M3 screws.

![led_matrices_edited_P1410430.jpg](https://cdn-learn.adafruit.com/assets/assets/000/124/412/medium640/led_matrices_edited_P1410430.jpg?1694713822)

Small 1x2 brackets secure the matrices together along the seams with M3 screws.

![led_matrices_edited_P1410432.jpg](https://cdn-learn.adafruit.com/assets/assets/000/124/413/medium640/led_matrices_edited_P1410432.jpg?1694713870)

# LED Matrix Sports Scoreboard

## Prep the Team Logos

One of the biggest challenges for this project is how to gather all of the team logos since it isn't just a matter of downloading them but also formatting them so that they will work with and look nice on an RGB LED matrix. To handle all of this you can run the **get\_team\_logos.py** Python script on your desktop or laptop computer.

https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/main/Matrix_Portal/Matrix_Portal_S3_ESPN_API/get_team_logos.py

To run the script you will need a desktop or laptop computer with Python 3 installed. A Raspberry Pi running Raspberry Pi OS would also be a great option.

## Python Dependencies

First, you'll use `pip` to install the Python libraries required to run the script:

```terminal
pip install pillow
pip install requests
```

## Customize the Script

Open the **get\_team\_logos.py** script in your preferred text editor or IDE. At the top of the code, you can customize which sports and corresponding leagues you want to gather the logos for. Edit the `sport_names` array with the name of the sport (baseball, basketball, etc) and edit the `sport_leagues` array with the corresponding name of the league (MLB, NBA, etc).

```python
import os
import math
import requests
from PIL import Image

# the name of the sports you want to follow
sport_names = ["football", "baseball", "soccer", "hockey", "basketball"]
# the name of the corresponding leages you want to follow
sport_leagues = ["nfl", "mlb", "usa.1", "nhl", "nba"]
# directory to match CircuitPython code folder names
bitmap_directories = ["team0_logos", "team1_logos", "team2_logos", "team3_logos", "team4_logos"]
```

Each set of team logos will be saved in the corresponding folders named in the `bitmap_directories` array. These folder names are utilized in the CircuitPython code that will run on your Matrix Portal S3.

## Run the Script

After customizing the script, you can run the **get\_team\_logos.py** script on your computer with:

```terminal
python get_team_logos.py
```

As the script runs, you'll see the download status for each team logo scroll by.

![led_matrices_get_team_logo_terminal.png](https://cdn-learn.adafruit.com/assets/assets/000/124/449/medium640/led_matrices_get_team_logo_terminal.png?1694808673)

When the script finishes, you'll see a folder called **/sport\_logos** in the same directory where you have the **get\_team\_logos.py** script saved. If you go into the **/sport\_logos** folder, you'll see the folders containing the team logos. You'll want to drag and drop these five folders onto your Matrix Portal S3 **CIRCUITPY** drive.

![led_matrices_logo_folders.png](https://cdn-learn.adafruit.com/assets/assets/000/124/450/medium640/led_matrices_logo_folders.png?1694809216)

Info: 

## How the Script Works

The script creates a folder called **/sport\_logos** in the same directory where you have the script saved. Then it fetches the ESPN API Teams JSON feed associated with each league that you defined at the top of the script. There is a URL for each team logo in that feed. The logo .PNG image file is downloaded to your computer. Then it is resized to be 32x32 pixels and run thru the `process()` function to add gamma correction and save the image as a bitmap. The script finishes by deleting the previously downloaded .PNG image since you don't need it anymore.

This script utilizes the `process()` function from the&nbsp;[protomatter\_dither.py](https://raw.githubusercontent.com/adafruit/Adafruit_Media_Converters/master/protomatter_dither.py)&nbsp;script that is used in the&nbsp;[Image Correction for RGB LED Matrices](https://learn.adafruit.com/image-correction-for-rgb-led-matrices/overview)&nbsp;guide. That script provides gamma correction for the images so that they look beautiful on RGB LED matrices. By including the function in the&nbsp; **get\_team\_logos.py** , all of the images are automatically converted without having to run another script.

[Image Correction for RGB LED Matrices Learn Guide](https://learn.adafruit.com/image-correction-for-rgb-led-matrices/overview)
# LED Matrix Sports Scoreboard

## Install CircuitPython

[CircuitPython](https://github.com/adafruit/circuitpython) is a derivative of [MicroPython](https://micropython.org) designed to simplify experimentation and education on low-cost microcontrollers. It makes it easier than ever to get prototyping by requiring no upfront desktop software downloads. Simply copy and edit files on the **CIRCUITPY** drive to iterate.

## Set up CircuitPython Quick Start!

Follow this quick step-by-step for super-fast Python power :)

Info: 

[Download the latest version of CircuitPython for this board via circuitpython.org](https://circuitpython.org/board/adafruit_matrixportal_s3/)
## Further Information

For more detailed info on installing CircuitPython, check out [Installing CircuitPython](https://learn.adafruit.com/welcome-to-circuitpython/installing-circuitpython).

 **Click the link above and download the latest UF2 file.**

Download and save it to your desktop (or wherever is handy).

![led_matrices_Save_to_Desktop.png](https://cdn-learn.adafruit.com/assets/assets/000/095/075/medium640/led_matrices_Save_to_Desktop.png?1601050695)

Plug your MatrixPortal S3 into your computer using a known-good USB cable.

**A lot of people end up using charge-only USB cables and it is very frustrating! So make sure you have a USB cable you know is good for data sync.**

Click the **Reset** button (indicated by the green arrow) on your board. When you see the NeoPixel RGB LED (indicated by the magenta arrow) turn purple, press it again. At that point, the NeoPixel should turn green. If it turns red, check the USB cable, try another USB port, etc.

If double-clicking doesn't work the first time, try again. Sometimes it can take a few tries to get the rhythm right!

![led_matrices_Buttons.jpg](https://cdn-learn.adafruit.com/assets/assets/000/126/017/medium640/led_matrices_Buttons.jpg?1699492993)

Info: 

You will see a new disk drive appear called **MATRXS3BOOT**.

Drag the **adafruit\_circuitpython\_etc.uf2** file over to **MATRXS3BOOT****.**

![led_matrices_matrix.png](https://cdn-learn.adafruit.com/assets/assets/000/126/018/medium640/led_matrices_matrix.png?1699493436)

The LED will flash. Then, the **MATRXS3BOOT** &nbsp;drive will disappear and a new disk drive called **CIRCUITPY** will appear.

That's it, you're done! :)

![led_matrices_CircuitPy.png](https://cdn-learn.adafruit.com/assets/assets/000/122/618/medium640/led_matrices_CircuitPy.png?1689369656)

# LED Matrix Sports Scoreboard

## Create Your settings.toml File

CircuitPython works with WiFi-capable boards to enable you to make projects that have network connectivity. This means working with various passwords and API keys. As of [CircuitPython 8](https://circuitpython.org/downloads), there is support for a **settings.toml** file. This is a file that is stored on your **CIRCUITPY** drive, that contains all of your secret network information, such as your SSID, SSID password and any API keys for IoT services. It is designed to separate your sensitive information from your **code.py** file so you are able to share your code without sharing your credentials.

CircuitPython previously used a **secrets.py** file for this purpose. The **settings.toml** file is quite similar.

Warning: Your **settings.toml** file should be stored in the main directory of your **CIRCUITPY** drive. It should not be in a folder.

## CircuitPython **settings.toml** File

This section will provide a couple of examples of what your **settings.toml** file should look like, specifically for CircuitPython WiFi projects in general.

The most minimal **settings.toml** file must contain your WiFi SSID and password, as that is the minimum required to connect to WiFi. Copy this example, paste it into your **settings.toml** , and update:

- `your_wifi_ssid`
- `your_wifi_password`

```auto
CIRCUITPY_WIFI_SSID = "your_wifi_ssid"
CIRCUITPY_WIFI_PASSWORD = "your_wifi_password"
```

Many CircuitPython network-connected projects on the Adafruit Learn System involve using Adafruit IO. For these projects, you must _also_ include your Adafruit IO username and key. Copy the following example, paste it into your settings.toml file, and update:

- `your_wifi_ssid`
- `your_wifi_password`
- `your_aio_username`
- `your_aio_key`

```auto
CIRCUITPY_WIFI_SSID = "your_wifi_ssid"
CIRCUITPY_WIFI_PASSWORD = "your_wifi_password"
ADAFRUIT_AIO_USERNAME = "your_aio_username"
ADAFRUIT_AIO_KEY = "your_aio_key"
```

Some projects use different variable names for the entries in the **settings.toml** file. For example, a project might use `ADAFRUIT_AIO_ID` in the place of `ADAFRUIT_AIO_USERNAME`. **If you run into connectivity issues, one of the first things to check is that the names in the settings.toml file match the names in the code.**

Warning: Not every project uses the same variable name for each entry in the **settings.toml** file! Always verify it matches the code.

## **settings.toml** File Tips
Here is an example **settings.toml** file.

```auto
# Comments are supported
CIRCUITPY_WIFI_SSID = "guest wifi"
CIRCUITPY_WIFI_PASSWORD = "guessable"
CIRCUITPY_WEB_API_PORT = 80
CIRCUITPY_WEB_API_PASSWORD = "passw0rd"
test_variable = "this is a test"
thumbs_up = "\U0001f44d"
```

In a **settings.toml** file, it's important to keep these factors in mind:

- Strings are wrapped in double quotes; ex: `"your-string-here"`
- Integers are _ **not** _ quoted and may be written in decimal with optional sign (`+1`, `-1`, `1000`) or hexadecimal (`0xabcd`).
  - Floats (decimal numbers), octal (`0o567`) and binary (`0b11011`) are not supported.

- Use `\u` escapes for weird characters, `\x` and `\ooo` escapes are not available in **.toml** files
  - Example: `\U0001f44d` for 👍 (thumbs up emoji) and `\u20ac` for € (EUR sign)

- Unicode emoji, and non-ASCII characters, stand for themselves as long as you're careful to save in "UTF-8 without BOM" format

&nbsp;

&nbsp;

When your&nbsp; **settings.toml&nbsp;** file is ready, you can save it in your text editor with the **.toml** &nbsp;extension.

![adafruit_products_dotToml.jpg](https://cdn-learn.adafruit.com/assets/assets/000/117/071/medium640/adafruit_products_dotToml.jpg?1671034293)

## Accessing Your **settings.toml** Information in **code.py**
In your **code.py** file, you'll need to `import` the `os` library to access the **settings.toml** file. Your settings are accessed with the `os.getenv()` function. You'll pass your settings entry to the function to import it into the **code.py** file.

```python
import os

print(os.getenv("test_variable"))
```

![](https://cdn-learn.adafruit.com/assets/assets/000/117/072/medium800/adafruit_products_tomlOutput.jpg?1671034496)

In the upcoming CircuitPython WiFi examples, you'll see how the **settings.toml&nbsp;** file is used for connecting to your SSID and accessing your API keys.

# LED Matrix Sports Scoreboard

## Code the Scoreboard

Once you've finished setting up your Matrix Portal S3 with CircuitPython, you can access the code and necessary libraries by downloading the Project Bundle.

To do this, click on the **Download Project Bundle** button in the window below. It will download to your computer as a zipped folder.

https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/main/Matrix_Portal/Matrix_Portal_S3_ESPN_API/CircuitPython/code.py

## Upload the Code and Libraries to the Matrix Portal S3

After downloading the Project Bundle, plug your Matrix Portal S3 into the computer's USB port with a known good USB data+power cable. You should see a new flash drive appear in the computer's File Explorer or Finder (depending on your operating system) called **CIRCUITPY**. Unzip the folder and copy the following items to the Matrix Portal S3's **CIRCUITPY** drive.

- **lib** folder
- **code.py**

Your Matrix Portal S3 **CIRCUITPY** drive should look like this after copying the **lib** folder and the **code.py** file.

![CIRCUITPY](https://adafruit.github.io/Adafruit_Learning_System_Guides/Matrix_Portal_S3_ESPN_API_CircuitPython.png )

## Add Your **settings.toml** File

As of CircuitPython 8.0.0, there is support for [Environment Variables](https://docs.circuitpython.org/en/latest/docs/environment.html). Environment variables are stored in a **settings.toml** file. Similar to **secrets.py** , the **settings.toml** file separates your sensitive information from your main **code.py** file. Add your **settings.toml** file as described in the [Create Your settings.toml File page](https://learn.adafruit.com/led-matrix-sports-scoreboard/create-your-settings-toml-file) earlier in this guide. You'll need to include your `CIRCUITPY_WIFI_SSID` and `CIRCUITPY_WIFI_PASSWORD`.

```python
CIRCUITPY_WIFI_SSID = "your-ssid-here"
CIRCUITPY_WIFI_PASSWORD = "your-ssid-password-here"
```

## Add Your Team Logo Bitmaps

You'll need to run the **get\_team\_logos.py** script that is included on the [Prep the Team Logos page](https://learn.adafruit.com/led-matrix-sports-scoreboard/prep-the-team-logos) earlier in this guide to download the team logo bitmaps to display on the RGB LED matrices.

[Prep the Team Logos Guide Page](https://learn.adafruit.com/led-matrix-sports-scoreboard/prep-the-team-logos)
When the script finishes, you'll see a folder called **/sport\_logos** in the same directory where you have the **get\_team\_logos.py** script saved. If you go into the **/sport\_logos** folder, you'll see the folders containing the team logos:

- **/team0\_logos**
- **/team1\_logos**
- **/team2\_logos**
- **/team3\_logos**
- **/team4\_logos**

You'll drag and drop these five folders onto the main directory of your Matrix Portal S3 **CIRCUITPY** drive.

![led_matrices_logo_folders.png](https://cdn-learn.adafruit.com/assets/assets/000/124/451/medium640/led_matrices_logo_folders.png?1694810282)

Warning: 

## How the CircuitPython Code Works

At the top of the code you can find all of the parameters that you can edit to customize the project. You can add your time zone, sports, leagues and team names. There are also two timers: `fetch_timer` determines how often the API should be pinged and `display_timer` determines the speed of the team display rotation.

```python
# font color for text on matrix
font_color = 0xFFFFFF
# your timezone UTC offset and timezone name
timezone_info = [-4, "EDT"]
# the name of the sports you want to follow
sport_name = ["football", "baseball", "soccer", "hockey", "basketball"]
# the name of the corresponding leages you want to follow
sport_league = ["nfl", "mlb", "usa.1", "nhl", "nba"]
# the team names you want to follow
# must match the order of sport/league arrays
# include full name and then abbreviation (usually city/region)
team0 = ["New England Patriots", "NE"]
team1 = ["Boston Red Sox", "BOS"]
team2 = ["New England Revolution", "NE"]
team3 = ["Boston Bruins", "BOS"]
team4 = ["Boston Celtics", "BOS"]
# how often the API should be fetched
fetch_timer = 300 # seconds
# how often the display should update
display_timer = 30 # seconds
```

## Matrix

An `RGBMatrix` object is instantiated to be 128 pixels wide by 64 pixels high. It is then passed to be a `FramebufferDisplay`.

```python
# matrix setup
base_width = 64
base_height = 32
chain_across = 2
tile_down = 2
DISPLAY_WIDTH = base_width * chain_across
DISPLAY_HEIGHT = base_height * tile_down
matrix = rgbmatrix.RGBMatrix(
    width=DISPLAY_WIDTH, height=DISPLAY_HEIGHT, bit_depth=3,
    rgb_pins=[
        board.MTX_R1,
        board.MTX_G1,
        board.MTX_B1,
        board.MTX_R2,
        board.MTX_G2,
        board.MTX_B2
    ],
    addr_pins=[
        board.MTX_ADDRA,
        board.MTX_ADDRB,
        board.MTX_ADDRC,
        board.MTX_ADDRD
    ],
    clock_pin=board.MTX_CLK,
    latch_pin=board.MTX_LAT,
    output_enable_pin=board.MTX_OE,
    tile=tile_down, serpentine=True,
    doublebuffer=False
)
display = framebufferio.FramebufferDisplay(matrix)
```

## URLs

The ESPN API URLs are added to the `SPORT_URLS` array by passing the entries from the `sport_name` and `sport_league` arrays to the base URL with an f-string.

```python
# add API URLs
SPORT_URLS = []
for i in range(5):
    d = (
    f"https://site.api.espn.com/apis/site/v2/sports/{sport_name[i]}/{sport_league[i]}/scoreboard"
    )
    SPORT_URLS.append(d)
```

## Logos

Each team logo is added to the `logos` array. The logos are named for the team abbreviations, which allows the code to pass the second entry from the `team#` array to find the logo in the appropriate logo folder.

```python
# arrays for teams, logos and display groups
teams = []
logos = []
groups = []
# add team to array
teams.append(team0)
# grab logo bitmap name
logo0 = "/team0_logos/" + team0[1] + ".bmp"
# add logo to array
logos.append(logo0)
# create a display group
group0 = displayio.Group()
# add group to array
groups.append(group0)
# repeat:
```

## Start-Up Screen

The `sport_startup()` function runs at the start of the code. It displays all five of your team logos.

```python
# initial startup screen
# shows the five team logos you are following
def sport_startup(logo):
    try:
        group = displayio.Group()
        bitmap0 = displayio.OnDiskBitmap(logo[0])
        grid0 = displayio.TileGrid(bitmap0, pixel_shader=bitmap0.pixel_shader, x = 0)
        bitmap1 = displayio.OnDiskBitmap(logo[1])
        grid1 = displayio.TileGrid(bitmap1, pixel_shader=bitmap1.pixel_shader, x = 32)
        bitmap2 = displayio.OnDiskBitmap(logo[2])
        grid2 = displayio.TileGrid(bitmap2, pixel_shader=bitmap2.pixel_shader, x = 64)
        bitmap3 = displayio.OnDiskBitmap(logo[3])
        grid3 = displayio.TileGrid(bitmap3, pixel_shader=bitmap3.pixel_shader, x = 96)
        bitmap4 = displayio.OnDiskBitmap(logo[4])
        grid4 = displayio.TileGrid(bitmap4, pixel_shader=bitmap4.pixel_shader, x = 48, y=32)
        group.append(grid0)
        group.append(grid1)
        group.append(grid2)
        group.append(grid3)
        group.append(grid4)
        display.root_group = group
    # pylint: disable=broad-except
    except Exception:
        print("Can't find bitmap. Did you run the get_team_logos.py script?")
```

## UTC to Your Time Zone

The `convert_date_format()` function is used to reformat the time that is fetched from the ESPN API. It rearranges the string to be formatted as "MM/DD - HH:MM AM/PM TZ" and converts the time from UTC to your defined time zone.

```python
# takes UTC time from JSON and reformats how its displayed
def convert_date_format(date, tz_information):
    # extract year, month, day, hour, and minute from the string
    year = int(date[0:4])
    month = int(date[5:7])
    day = int(date[8:10])
    hour = int(date[11:13])
    minute = int(date[14:16])
    # make a datetime object using the extracted values
    dt = datetime(year, month, day, hour, minute)
    # adjust the datetime object for the time zone
    dt_adjusted = dt + timedelta(hours=tz_information[0])
    # pull out the data from the datetime
    month = dt_adjusted.month
    day = dt_adjusted.day
    hour = dt_adjusted.hour
    minute = dt_adjusted.minute
    # convert 24 hour time to 12 hour time and determine AM/PM
    am_pm = "AM" if hour &lt; 12 else "PM"
    hour_12 = hour if hour &lt;= 12 else hour - 12
    minute = f"{minute:02}"
    # bring in time zone name for display
    time_zone_str = tz_information[1]
    return f"{month}/{day} - {hour_12}:{minute} {am_pm} {time_zone_str}"
```

## MVP of the Code: Fetch the Data

The `get_data()` function takes care of making a request to the ESPN API and updating the display attributes for each team group. The URL, team, team logo and team display group are passed to the function.

The function starts by bringing in the team logo as an `OnDiskBitmap` and prepping the different text elements.

```python
def get_data(data, team, logo, group):
    pixel.fill((0, 0, 255))
    print(f"Fetching data from {data}")
    playing = False
    names = []
    scores = []
    info = []
    # the team you are following's logo
    bitmap0 = displayio.OnDiskBitmap(logo)
    grid0 = displayio.TileGrid(bitmap0, pixel_shader=bitmap0.pixel_shader, x = 2)
    home_text = adafruit_display_text.label.Label(terminalio.FONT, color=font_color,
                                                  text=" ")
    away_text = adafruit_display_text.label.Label(terminalio.FONT, color=font_color,
                                                  text=" ")
    vs_text = adafruit_display_text.label.Label(terminalio.FONT, color=font_color,
                                                text=" ")
    vs_text.anchor_point = (0.5, 0.0)
    vs_text.anchored_position = (DISPLAY_WIDTH / 2, 14)
    info_text = adafruit_display_text.label.Label(terminalio.FONT, color=font_color,
                                                  text=" ")
    info_text.anchor_point = (0.5, 1.0)
    info_text.anchored_position = (DISPLAY_WIDTH / 2, DISPLAY_HEIGHT)
```

Then the request is made to the ESPN API using the `json_stream` library. This streams the JSON rather than storing the entire JSON response. This is a lot faster and uses less memory. However, because the data is literally streaming by, you have to be thoughtful about how you are logging the information from the JSON. JSON entries have to be accessed in the order in which they are listed in the JSON response. This is why the `date` entry is continually added and cleared from the `info` array, since it is listed before you know if it matches your team's game.

```python
# make the request to the API
    resp = requests.get(data)
    # stream the json
    json_data = json_stream.load(resp.iter_content(32))
    for event in json_data["events"]:
        # clear the date and then add the date to the array
        # the date for your game will remain
        info.clear()
        info.append(event["date"])
        # check for your team playing
        if team[0] not in event["name"]:
            continue
        for competition in event["competitions"]:
            for competitor in competition["competitors"]:
                # if your team is playing:
                playing = True
                # get team names
                # index indicates home vs. away
                names.append(competitor["team"]["abbreviation"])
                # the current score
                scores.append(competitor["score"])
            # gets info on game
            info.append(event["status"]["type"]["shortDetail"])
        break
```

If your team shows up in the API response, the date is converted using the `convert_date_format()` function. There are checks to see if the game is active and which team is home and away. These checks determine the content of the text elements. The opposing team's logo is accessed by replacing your team's abbreviation name with the opposing team's abbreviation.&nbsp;

```python
if playing:
        # pull out the date
        date = info[0]
        # convert it to be readable
        date = convert_date_format(date, timezone_info)
        print(date)
        # pull out the info
        info = info[1]
        # check if it's pre-game
        if str(info) == date or str(info) == "Scheduled":
            status = "pre"
            print("match, pre-game")
        else:
            status = info
        # home and away text
        # teams index determines which team is home or away
        home_text.text="HOME"
        away_text.text="AWAY"
        if team[1] is names[0]:
            home_game = True
            home_text.anchor_point = (0.0, 0.5)
            home_text.anchored_position = (5, 37)
            away_text.anchor_point = (1.0, 0.5)
            away_text.anchored_position = (124, 37)
            vs_team = names[1]
        else:
            home_game = False
            away_text.anchor_point = (0.0, 0.5)
            away_text.anchored_position = (5, 37)
            home_text.anchor_point = (1.0, 0.5)
            home_text.anchored_position = (124, 37)
            vs_team = names[0]
        # if it's pre-game, show "VS"
        if status == "pre":
            vs_text.text="VS"
            info_text.text=date
        # if it's active or final show score
        else:
            info_text.text=info
            if home_game:
                vs_text.text=f"{scores[0]} - {scores[1]}"
            else:
                vs_text.text=f"{scores[1]} - {scores[0]}"
        # load in logo from other team
        vs_logo = logo.replace(team[1], vs_team)
```

If your team does not match any of the entries in the JSON response, then your team's logo is shown twice and the text "`NO DATA AVAILABLE`" is shown.

```python
# if there is no game matching your team:
    else:
        status = "pre"
        vs_logo = logo
        info_text.text="NO DATA AVAILABLE"
```

Finally the display group is updated with the logo and text elements and the response is closed.

```python
# update the display group. try/except in case its the first time it's being added
    try:
        group[0] = grid0
        group[1] = grid1
        group[2] = home_text
        group[3] = away_text
        group[4] = vs_text
        group[5] = info_text
    except IndexError:
        group.append(grid0)
        group.append(grid1)
        group.append(home_text)
        group.append(away_text)
        group.append(vs_text)
        group.append(info_text)
    # close the response
    resp.close()
```

## The Loop

Two processes are happening concurrently in the loop with the help of `ticks`. The matrices are cycling through the five sport display groups by advancing through the `groups` array.

```python
# update display seperate from API request
        if ticks_diff(ticks_ms(), display_clock) &gt;= display_timer:
            print("updating display")
            display.root_group = groups[display_index]
            display_index = (display_index + 1) % len(teams)
            display_clock = ticks_add(display_clock, display_timer)
```

The different ESPN API URLs are being fetched on a rotation. The display groups are updated in the `get_data()` function. As a result, when a team display group is shown it will have the updated data.

```python
if not just_fetched:
  # garbage collection for display groups
  gc.collect()
  # fetch the json for the next team
  just_fetched = get_data(SPORT_URLS[fetch_index],
                          teams[fetch_index],
                          logos[fetch_index],
                          groups[fetch_index])
  # advance index
  fetch_index = (fetch_index + 1) % len(teams)
  # reset clocks
  fetch_clock = ticks_add(fetch_clock, fetch_timer)
  display_clock = ticks_add(display_clock, display_timer)
# cleared for fetching after time has passed
if ticks_diff(ticks_ms(), fetch_clock) &gt;= fetch_timer:
  just_fetched = False
```

All of this is wrapped in a try/except in case any errors occur while making a request to the ESPN API. If an error occurs, the Matrix Portal S3 resets itself.

```python
try:
  ...
except Exception as Error:
        print(Error)
        time.sleep(10)
        gc.collect()
        time.sleep(5)
        microcontroller.reset()
```

# LED Matrix Sports Scoreboard

## Wiring and Assembly

![](https://cdn-learn.adafruit.com/assets/assets/000/124/415/medium800/led_matrices_editedWithAnotation_P1410435.jpg?1694715310)

Making sure your matrices are laid out in the correct order can be confusing. Before plugging in any cables, lay them out to make sure they are oriented properly. Each matrix has arrow markings which you can use to help during layout.

- Matrix 1 - This matrix will have the Matrix Portal S3 plugged into its IDC port on the left. Its arrow markings will be pointing up and to the right.
- Matrix 2 - This matrix is placed to the right of Matrix 1. Its arrow markings will be pointing up and to the right.
- Matrix 3 - This matrix is placed below Matrix 2. Its arrow markings will be pointing down and to the left.
- Matrix 4 - This matrix is placed to the left of Matrix 3 and below Matrix 1. Its arrow markings will be pointing down and to the left.

Once you have your four matrices laid out in the correct order you can start plugging in the IDC cables.

Plug an IDC cable into the right-hand port on Matrix 1 and the left-hand port on Matrix 2.

![led_matrices_editedAnotated_P1410436.jpg](https://cdn-learn.adafruit.com/assets/assets/000/124/416/medium640/led_matrices_editedAnotated_P1410436.jpg?1694715749)

Plug an IDC cable into the right-hand port on Matrix 2 and the right-hand port on Matrix 3.

![led_matrices_editedAnotated_P1410437.jpg](https://cdn-learn.adafruit.com/assets/assets/000/124/417/medium640/led_matrices_editedAnotated_P1410437.jpg?1694715843)

Plug the last IDC cable into the left-hand port on the Matrix 3 and the right-hand port on the Matrix 4. This completes the data wiring for the matrices.

![led_matrices_editedAnnotated_P1410438.jpg](https://cdn-learn.adafruit.com/assets/assets/000/124/418/medium640/led_matrices_editedAnnotated_P1410438.jpg?1694715918)

## 3D Printed Brackets
Place the center bracket over the intersection in the middle of the four matrices. Use the bracket to make sure that the matrices are aligned with each other. Secure it with four M3 screws.

![led_matrices_editedAnnotated_P1410439.jpg](https://cdn-learn.adafruit.com/assets/assets/000/124/419/medium640/led_matrices_editedAnnotated_P1410439.jpg?1694716056)

Use four 2x1 brackets to join the matrices together to the left and right of the center bracket. Secure the brackets with M3 screws.

![led_matrices_editedAnnotated_P1410441.jpg](https://cdn-learn.adafruit.com/assets/assets/000/124/421/medium640/led_matrices_editedAnnotated_P1410441.jpg?1694716181)

Use two 2x1 brackets to secure the matrices above and below the center bracket. Secure the brackets with M3 screws.

![led_matrices_editedAnnotated_P1410442.jpg](https://cdn-learn.adafruit.com/assets/assets/000/124/422/medium640/led_matrices_editedAnnotated_P1410442.jpg?1694716258)

## Power
Gather two power cables that came with your matrices. Plug one of the cables into the two power inputs on the top two matrices. Plug the other cable into the two power inputs on the bottom two matrices.

![led_matrices_editedAnnotated_P1410444.jpg](https://cdn-learn.adafruit.com/assets/assets/000/124/423/medium640/led_matrices_editedAnnotated_P1410444.jpg?1694716348)

Each power cable connector will be secured in a DC jack terminal block adapter.

![led_matrices_edited_P1410446.jpg](https://cdn-learn.adafruit.com/assets/assets/000/124/424/medium640/led_matrices_edited_P1410446.jpg?1694716410)

Secure the positive cable (red wire) into the positive terminal on the DC jack adapter (labeled with a raised +). Secure the ground cable (black wire) into the negative terminal on the DC jack adapter (labeled with a raised -). Repeat this for the second power cable.

![led_matrices_edited_P1410455.jpg](https://cdn-learn.adafruit.com/assets/assets/000/124/425/medium640/led_matrices_edited_P1410455.jpg?1694716443)

Now you can plug both sets of two matrices into 5V 4A power supplies.

## LED Acrylic
Remove the protective film from the shiny side of the LED acrylic.

![led_matrices_edited_P1410463.jpg](https://cdn-learn.adafruit.com/assets/assets/000/124/452/medium640/led_matrices_edited_P1410463.jpg?1695043441)

Place mounting tabs in the four corners of the acrylic.

![led_matrices_edited_P1410464.jpg](https://cdn-learn.adafruit.com/assets/assets/000/124/453/medium640/led_matrices_edited_P1410464.jpg?1695043512)

Attach the acrylic to one of the RGB LED panels, lining it up with the edges of the panel.

![led_matrices_edited_P1410465.jpg](https://cdn-learn.adafruit.com/assets/assets/000/124/454/medium640/led_matrices_edited_P1410465.jpg?1695043533)

Repeat this process for the three remaining RGB LED panels. Remove the protective film from the acrylic sheets.

![led_matrices_edited_P1410468.jpg](https://cdn-learn.adafruit.com/assets/assets/000/124/455/medium640/led_matrices_edited_P1410468.jpg?1695043581)

# LED Matrix Sports Scoreboard

## Use and Customization

![](https://cdn-learn.adafruit.com/assets/assets/000/125/028/medium800/led_matrices_edited_P1410514.jpg?1696862980)

This project is meant to be customized to follow your favorite sports and teams. You can&nbsp; do this by editing the parameters at the top of the **code.py** file.

### Time Zone

The ESPN API stores the timestamp for games in UTC. There is a function in the code that converts the UTC time to your defined time zone. You'll add your time zone UTC offset and time zone name into the `timezone_info` array:

```python
# your timezone UTC offset and timezone name
timezone_info = [-4, "EDT"]
```

### Sports and Teams

The `sport_name` and `sport_league` array determine which ESPN API feeds are fetched in the code. You'll add the name of the sport (football, basketball, etc) to the `sport_name` array and the corresponding league (NFL, NBA, etc.) to the `sport_league` array.&nbsp;

```python
# the name of the sports you want to follow
sport_name = ["football", "baseball", "soccer", "hockey", "basketball"]
# the name of the corresponding leagues you want to follow
sport_league = ["nfl", "mlb", "usa.1", "nhl", "nba"]
```

You'll add your team names to the `team0` thru `team4` arrays. You'll include the full name of the team, followed by the team abbreviation. If you aren't sure about your team information, you can open the ESPN API JSON URL in a browser and find your team to see how it is being referred to by the API.

```python
# the team names you want to follow
# must match the order of sport/league arrays
# include full name and then abbreviation (usually city/region)
team0 = ["New England Patriots", "NE"]
team1 = ["Toronto Blue Jays", "TOR"]
team2 = ["Chicago Fire FC", "CHI"]
team3 = ["Los Angeles Kings", "LA"]
team4 = ["Minnesota Timberwolves", "MIN"]
```

### Timers

Two timers are used in the code loop. The `fetch_timer` determines how often the API is fetched by the Matrix Portal S3. The `display_timer` determines the speed at which the matrices scroll thru your different team data.

```python
# how often the API should be fetched
fetch_timer = 300 # seconds
# how often the display should update
display_timer = 60 # seconds
```

## Use
When the Matrix Portal S3 boots up, all five of your team logos will be displayed.

![led_matrices_edited_P1410470.jpg](https://cdn-learn.adafruit.com/assets/assets/000/124/459/medium640/led_matrices_edited_P1410470.jpg?1695047801)

If a game is scheduled for your team, the date and time in your time zone are shown at the bottom of the matrices. Your team is always shown on the left and the opposing team is shown on the right. "HOME" and "AWAY" information are shown below the team logos.

![led_matrices_edited_P1410481.jpg](https://cdn-learn.adafruit.com/assets/assets/000/124/461/medium640/led_matrices_edited_P1410481.jpg?1695047870)

While a game is happening, the score and game details are shown on the matrices. The details will vary depending on the sport.

![led_matrices_edited_P1410476.jpg](https://cdn-learn.adafruit.com/assets/assets/000/124/460/medium640/led_matrices_edited_P1410476.jpg?1695047834)

When a game is completed, the final score is shown along with the text "Final" at the bottom of the matrices.

![led_matrices_edited_P1410478.jpg](https://cdn-learn.adafruit.com/assets/assets/000/124/462/medium640/led_matrices_edited_P1410478.jpg?1695047980)

If your team is not found in the API response, then your team's logo is shown twice on the display along with "NO DATA AVAILABLE" at the bottom of the matrices. This could mean that the team does not have a game currently scheduled according to the JSON response or the league could be in the off-season.&nbsp;

![led_matrices_edited_P1410508.jpg](https://cdn-learn.adafruit.com/assets/assets/000/124/463/medium640/led_matrices_edited_P1410508.jpg?1695048011)


## Featured Products

### Adafruit Matrix Portal S3 CircuitPython Powered Internet Display

[Adafruit Matrix Portal S3 CircuitPython Powered Internet Display](https://www.adafruit.com/product/5778)
Folks love our [wide selection of RGB matrices](https://www.adafruit.com/category/327) and accessories&nbsp;for making custom colorful LED displays... and our RGB Matrix Shields and FeatherWings can be quickly soldered together to make the wiring much easier. But what if we made it...

Out of Stock
[Buy Now](https://www.adafruit.com/product/5778)
[Related Guides to the Product](https://learn.adafruit.com/products/5778/guides)
### 64x32 RGB LED Matrix - 4mm pitch

[64x32 RGB LED Matrix - 4mm pitch](https://www.adafruit.com/product/2278)
Bring a little bit of Times Square into your home with this sweet 64 x 32 square RGB LED matrix panel. These panels are normally used to make video walls, here in New York we see them on the sides of busses and bus stops, to display animations or short video clips. We thought they looked...

In Stock
[Buy Now](https://www.adafruit.com/product/2278)
[Related Guides to the Product](https://learn.adafruit.com/products/2278/guides)
### Black LED Diffusion Acrylic Panel - 10.2" x 5.1"

[Black LED Diffusion Acrylic Panel - 10.2" x 5.1"](https://www.adafruit.com/product/4749)
&nbsp;nice whoppin' rectangular slab of some lovely black acrylic to add some extra diffusion to your LED Matrix project. This material is 2.6mm (0.1") thick and is made of special cast acrylic that makes it perfect for glowy projects, especially matrices or NeoPixels.

Unlike...

In Stock
[Buy Now](https://www.adafruit.com/product/4749)
[Related Guides to the Product](https://learn.adafruit.com/products/4749/guides)
### 5V 4A (4000mA) switching power supply - UL Listed

[5V 4A (4000mA) switching power supply - UL Listed](https://www.adafruit.com/product/1466)
Need a lot of 5V power? This switching supply gives a clean regulated 5V output at up to **4 Amps** (4000mA). 110 or 240 input, so it works in any country. The plugs are "US 2-prong" style so you may need a plug adapter, but you can pick one up at any hardware store for $1 or so,...

Out of Stock
[Buy Now](https://www.adafruit.com/product/1466)
[Related Guides to the Product](https://learn.adafruit.com/products/1466/guides)
### Female DC Power adapter - 2.1mm jack to screw terminal block

[Female DC Power adapter - 2.1mm jack to screw terminal block](https://www.adafruit.com/product/368)
If you need to connect a DC power wall wart to a board that doesn't have a DC jack - this adapter will come in very handy! There is a 2.1mm DC jack on one end, and a screw terminal block on the other. The terminals are labeled with positive/negative assuming a positive-tip configuration...

In Stock
[Buy Now](https://www.adafruit.com/product/368)
[Related Guides to the Product](https://learn.adafruit.com/products/368/guides)
### Pink and Purple Woven USB A to USB C Cable - 1 meter long

[Pink and Purple Woven USB A to USB C Cable - 1 meter long](https://www.adafruit.com/product/5153)
This cable is not only super-fashionable, with a woven pink and purple Blinka-like pattern, it's also made for USB C for our modernized breakout boards, Feathers, and more.&nbsp;&nbsp;[If you want something just like it but for Micro B, we...](https://www.adafruit.com/product/4111)

Out of Stock
[Buy Now](https://www.adafruit.com/product/5153)
[Related Guides to the Product](https://learn.adafruit.com/products/5153/guides)
### Clear Adhesive Squares - 6 pack

[Clear Adhesive Squares - 6 pack](https://www.adafruit.com/product/4813)
 **UGlu Dashes** &nbsp;are perfect for a variety of small projects. These adhesive squares provide a stronger bond to most surfaces and are cleaner and easier to remove&nbsp;without the wait, mess, or hassle.

Adheres to a wide variety of surfaces, including, but not limited...

In Stock
[Buy Now](https://www.adafruit.com/product/4813)
[Related Guides to the Product](https://learn.adafruit.com/products/4813/guides)
### Black Nylon Machine Screw and Stand-off Set – M3 Thread

[Black Nylon Machine Screw and Stand-off Set – M3 Thread](https://www.adafruit.com/product/4685)
Totaling **420 pieces** , this **M3 Screw Set** &nbsp;is a must-have for your workstation.&nbsp;You'll have enough screws, nuts, and hex standoffs to fuel your maker tendencies&nbsp;for days on end! M3 size screws fit a number of&nbsp;Adafruit breakout/dev board...

In Stock
[Buy Now](https://www.adafruit.com/product/4685)
[Related Guides to the Product](https://learn.adafruit.com/products/4685/guides)

## Related Guides

- [Adafruit MatrixPortal S3](https://learn.adafruit.com/adafruit-matrixportal-s3.md)
- [Ocean Epoxy Resin Lightbox with RGB LED Matrix Image Scroller](https://learn.adafruit.com/ocean-epoxy-resin-lightbox-with-rgb-led-matrix-image-scroller.md)
- [AdaBox 016](https://learn.adafruit.com/adabox016.md)
- [Tombstone Prop-Maker RP2040](https://learn.adafruit.com/tombstone-prop-maker-rp2040.md)
- [MatrixPortal CircuitPython Animated Message Board](https://learn.adafruit.com/matrixportal-circuitpython-animated-message-board.md)
- [Adafruit RGB Matrix FeatherWings](https://learn.adafruit.com/rgb-matrix-featherwing.md)
- [Moon Phase Clock for Adafruit Matrix Portal](https://learn.adafruit.com/moon-phase-clock-for-adafruit-matrixportal.md)
- [Scroll an SMS Text Message on your RGB Matrix](https://learn.adafruit.com/scroll-an-sms-text-message-on-your-rgb-matrix.md)
- [Animated GIF Player for Matrix Portal](https://learn.adafruit.com/animated-gif-player-for-matrix-portal.md)
- [LED Matrix Scoreboard](https://learn.adafruit.com/led-matrix-scoreboard.md)
- [Matrix Portal Sand Handles](https://learn.adafruit.com/matrix-portal-sand.md)
- [RGB Matrix Slot Machine](https://learn.adafruit.com/rgb-matrix-slot-machine.md)
- [NextBus transit clock for Raspberry Pi](https://learn.adafruit.com/nextbus-transit-clock-for-raspberry-pi.md)
- [Matrix Portal New Guide Scroller](https://learn.adafruit.com/matrix-portal-new-guide-scroller.md)
- [Custom Scrolling Quote Board Matrix Display](https://learn.adafruit.com/aio-quote-board-matrix-display.md)
- [Adafruit 15x7 CharliePlex FeatherWing](https://learn.adafruit.com/adafruit-15x7-7x15-charlieplex-led-matrix-charliewing-featherwing.md)
