# MatrixPortal S3 Flight Proximity Tracker

## Overview

![](https://cdn-learn.adafruit.com/assets/assets/000/126/128/medium800thumb/leds_ezgif.com-resize-5.jpg?1700457771)

Warning: 

The Flight Proximity Tracker is a project that keeps you in the know about the nearby aerial traffic. This project displays real-time data of nearby aircraft using scrolling text, along with airline icons&nbsp;just like a departure board in an airport!

This project uses the **Matrix Portal S3** with CircuitPython. The Matrix Portal S3 fetches and process data from the [FlightAware's AeroAPI](https://www.flightaware.com/commercial/aeroapi/). Then the processed data will be displayed on two connected 64x64 LED matrix displays.

The display will not only indicate the flight identifier but also provide its origin along with the flight destination and, if you choose, altitude, speed, and other pertinent flight data.&nbsp;

## 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)

### Black Woven USB A to USB C Cable with 66W Watt Display - 1 meter

[Black Woven USB A to USB C Cable with 66W Watt Display - 1 meter](https://www.adafruit.com/product/5788)
Everyone has that "good charger" in the house, the one that seems to get your phone from 4% to 100% in record time. But it's a bit of a mystery isn't it? Why does one charger work great while another doesn't seem to budge at all. It has to do with whether that charger...

In Stock
[Buy Now](https://www.adafruit.com/product/5788)
[Related Guides to the Product](https://learn.adafruit.com/products/5788/guides)
![Angled shot of coiled black woven USB A to USB C cable.](https://cdn-shop.adafruit.com/640x480/5788-02.jpg)

### Part: 64x64 RGB LED Matrix - 2.5mm Pitch
quantity: 2
Two Panels are not required but will be used for the demonstration of this project
[64x64 RGB LED Matrix - 2.5mm Pitch](https://www.adafruit.com/product/3649)

### 64x64 RGB LED Matrix - 2.5mm Pitch

[64x64 RGB LED Matrix - 2.5mm Pitch](https://www.adafruit.com/product/3649)
Winter time can be rough in the city. The sky is gray. The weather&nbsp;is unpredictable. So slough off those seasonal blues with&nbsp;some&nbsp;Times Square razzle dazzle from this sweet, ultra-high-density **64x64 RGB LED Matrix**. These panels are typically used to make video...

In Stock
[Buy Now](https://www.adafruit.com/product/3649)
[Related Guides to the Product](https://learn.adafruit.com/products/3649/guides)
![Video of two hands flipping over a powered on 64x64 RGB LED Matrix. The matrix display has a falling colored sand effect. ](https://cdn-shop.adafruit.com/product-videos/640x480/3649-06.jpg)

### 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)

### 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)
![ 6 pack of Clear Adhesive Squares](https://cdn-shop.adafruit.com/640x480/4813-01.jpg)

### Mini Magnet Feet for RGB LED Matrices (Pack of 4)

[Mini Magnet Feet for RGB LED Matrices (Pack of 4)](https://www.adafruit.com/product/4631)
Got a glorious&nbsp;RGB Matrix project you want to mount and display in your workspace or home? If you have one of the matrix panels listed below, you'll need a pack of these **Mini-Magnet Feet.** &nbsp;We got these specifically&nbsp;for our RGB LED Matrices, which no longer...

In Stock
[Buy Now](https://www.adafruit.com/product/4631)
[Related Guides to the Product](https://learn.adafruit.com/products/4631/guides)
![Angled shot of four magnet feet.](https://cdn-shop.adafruit.com/640x480/4631-00.jpg)

# MatrixPortal S3 Flight Proximity Tracker

## Choosing APIs 

I've used three different APIs for this project. These are the APIs I passed on using and why:

### [OpenSky Network API](https://opensky-network.org)

If I couldn't use AeroAPI, I'd use [OpenSky Network API](https://opensky-network.org). One huge plus is that this API is open-source and **free**. You can acquire flight IDs, latitude and longitude, velocity, and other flight details.

One downside to using this API is that it does not provide commercial flight data such as airport schedules, delays,&nbsp;or similar information that cannot be derived from ADS-B data contents.

### [Aviationstack](https://aviationstack.com)

This API was difficult to use and expensive. The Free Plan offers 1,000 API requests per month.

The company claims to provide live tracking flight data:

> Thanks to real-time data integration with one of the most sophisticated aviation data vendors on the market, aviationstack delivers accurate flight data that you can use to build booking and tracking applications.

In contrast to the claim made by Aviationstack, my personal experience contradicts it. When I contacted their representative, I was told that I needed to upgrade my membership to a higher tier in order to access the live tracking data. Even after I opted for a monthly premium data subscription cost of $49.99, I still **couldn't access the required live tracking feature.**

Moreover, if you plan to use Aviationstack, please keep in mind that **if you need to cancel your subscription, you will have to send an email to the company and provide them with a valid reason for doing so.**

**Please note that the use of Aviationstack is solely at your own discretion.**

# MatrixPortal S3 Flight Proximity Tracker

## FlightAware API

![](https://cdn-learn.adafruit.com/assets/assets/000/126/093/medium800/leds_Screenshot_2023-11-17_at_11.17.25_AM.png?1700241201)

Warning: 

This project uses FlightAware's AeroAPI to retrieve the weather info through its API. To do so, you'll need to register for an account and get an API key.

To begin using the service, click on the **[link](https://www.flightaware.com/account/join/)**. This will take you to the registration page, where you can create a new account at no cost.

![leds_Screenshot_2023-11-17_at_12.14.04_PM.png](https://cdn-learn.adafruit.com/assets/assets/000/126/129/medium640/leds_Screenshot_2023-11-17_at_12.14.04_PM.png?1700490622)

Once you click the link, you'll be directed to a page where you'll need to **enter your login information**. Enter it to continue.

![leds_Screenshot_2023-11-17_at_11.19.14_AM.png](https://cdn-learn.adafruit.com/assets/assets/000/126/095/medium640/leds_Screenshot_2023-11-17_at_11.19.14_AM.png?1700241445)

After entering your login details, a confirmation email will be sent to you to activate your account

![leds_Screenshot_2023-11-17_at_11.20.29_AM.png](https://cdn-learn.adafruit.com/assets/assets/000/126/096/medium640/leds_Screenshot_2023-11-17_at_11.20.29_AM.png?1700241506)

Once you have activated your account, simply **click on the "Compare Tiers" option** to choose a subscription plan that fits your needs.

![leds_Screenshot_2023-11-17_at_11.23.08_AM.png](https://cdn-learn.adafruit.com/assets/assets/000/126/098/medium640/leds_Screenshot_2023-11-17_at_11.23.08_AM.png?1700241654)

Pink arrow:  
This is where we'll start the AeroAPI subscription process.

Red arrow:  
There is a monthly allowance of free usage—up to $5 free a month, which means that the service is free for users until their queries reach the cost equivalent of $5 within a month. Beyond this allowance, the user would need to pay per query.

**After that, you will be required to include your payment details.**

![leds_Screenshot_2023-11-17_at_11.23.45_AM.png](https://cdn-learn.adafruit.com/assets/assets/000/126/100/medium640/leds_Screenshot_2023-11-17_at_11.23.45_AM.png?1700242280)

## Subscription Cost Breakdown

Users are allowed to use the service for free up to a limit of $5 worth of queries per month. This means that the service is free until the user's queries reach the cost equivalent of $5 within a month. If the user crosses the limit, they would be required to pay for additional queries.

The allowance for ADS-B (Automatic Dependent Surveillance-Broadcast) feeds is double the usual limit, which means up to $10 worth of free usage per month. ADS-B is a surveillance technology used for tracking aircraft. Those who contribute data to the ADS-B network (feeders) might be rewarded with a higher free usage allowance as an incentive for providing valuable data to the service.

Okay - if FlightAware charges $0.05 per call, you might be curious about how to spread a $5 monthly budget evenly.

With $5, you can make 100 calls in a month. So, you can make roughly 3 calls per day without spending more than your budget. Think of it like a daily quota to keep your usage in check and avoid any surprise fees.

So, if there are 24 hours in a day, you want to make three calls in those 24 hours. The total hours by the number of calls: 24 hours / 3 calls = 8 hours per call.

**TL;DR: If you want to subscribe for free, you'll want to make up to three calls within a 24-hour period. Each call should be 8 hours apart.&nbsp;**

Once you have entered your payment information, you will be able to start using AeroAPI!

![leds_Screenshot_2023-11-17_at_11.40.12_AM.png](https://cdn-learn.adafruit.com/assets/assets/000/126/105/medium640/leds_Screenshot_2023-11-17_at_11.40.12_AM.png?1700244846)

Now that you have access to AeroAPI, visit this page to retrieve your generated API key.

![leds_Screenshot_2023-11-17_at_1.27.00_PM.png](https://cdn-learn.adafruit.com/assets/assets/000/126/106/medium640/leds_Screenshot_2023-11-17_at_1.27.00_PM.png?1700245807)

Here is your API key. It is crucial that you store this key in a secure location.

![leds_Screenshot_2023-11-17_at_1.27.35_PM.png](https://cdn-learn.adafruit.com/assets/assets/000/126/107/medium640/leds_Screenshot_2023-11-17_at_1.27.35_PM.png?1700245947)

Ok, now get some airline logos!

# MatrixPortal S3 Flight Proximity Tracker

## Preparing Airline Logos

One of the biggest challenges for this project is how to gather all of the airline 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 we wrote a Python script that checks against the existing airline abbreviations for available logos in the FlightAware API.

The logos are included in the project bundle, but if logos change over time or new airlines open you may want to run this script yourself.

https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/main/Matrix_Portal/MatrixPortal_S3_Flight_Proximity_Tracker/get_airline_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\_airline\_logos.py** script in your preferred text editor or IDE. At the top of the code, you can customize the folder name where the logo images will be saved and the size of the images.

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

# directory to match CircuitPython code folder names
bitmap_directories = "airline_logos"

img_width = 32
img_height = 32
```

## Run the Script

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

```terminal
python get_airline_logos.py
```

When the script finishes, you'll see a folder called **/airline\_logos** in the same directory where you have the **get\_airline\_logos.py** script saved. If you go into the **/airline\_logos** folder, you'll see the all of the airline logos. You'll want to drag and drop this folder onto your Matrix Portal S3 **CIRCUITPY** drive.

![leds_airlineLogoScreenshot.png](https://cdn-learn.adafruit.com/assets/assets/000/126/090/medium640/leds_airlineLogoScreenshot.png?1700233380)

Info: 

## How the Script Works

The script creates a folder called **/airline\_logos** in the same directory where you have the script saved. It tries letter and number combinations to compare against the database of airline abbreviations and logos available from the FlightAware API. When a matching airline logo is found, the logo .PNG image file is downloaded to your computer from the image URL hosted by the API. Then it is resized to be 32x32 pixels and run through 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 [protomatter\_dither.py](https://raw.githubusercontent.com/adafruit/Adafruit_Media_Converters/master/protomatter_dither.py) script that is used in the [Image Correction for RGB LED Matrices](https://learn.adafruit.com/image-correction-for-rgb-led-matrices/overview) guide. That script provides gamma correction for the images so that they look beautiful on RGB LED matrices. By including the function in the **get\_airline\_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)
# MatrixPortal S3 Flight Proximity Tracker

## 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)

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)

# MatrixPortal S3 Flight Proximity Tracker

## Setting Up Your Credentials

The Flight Proximity Tracker depends on you adding your WiFi and AeroAPI key to fetch JSON weather data.

Plug your Matrix Portal S3 into your computer via a known good data + power USB cable. Your board should show up as a thumb drive in your File Explorer / Finder (depending on your operating system) named **CIRCUITPY**.

Create a file with the name&nbsp; **settings.toml** &nbsp;in the root directory of the&nbsp; **CIRCUITPY** &nbsp;drive.

Add the following below:

The file should contain the keys&nbsp;`CIRCUITPY_WIFI_SSID`, &nbsp;`CIRCUITPY_WIFI_PASSWORD`, `CIRCUITPY_WEB_API_PASSWORD`,&nbsp; and `AERO_API_KEY`.&nbsp;

Once these are defined, CircuitPython will automatically connect to the network and start the webserver used for the workflow.

The web server is on port 80 unless overridden by&nbsp;`CIRCUITPY_WEB_API_PORT`. It also enables MDNS.

Here is an example&nbsp; **settings.toml** :

```python
# To auto-connect to WiFi
CIRCUITPY_WIFI_SSID="YOUR-WIFI-NETWORK-NAME"
CIRCUITPY_WIFI_PASSWORD="YOUR-WIFI-NETWORK-PASSWORD"

# For AeroAPI
AERO_API_KEY="A-LONG-STRING-OF-INTS-AND-CHARACTERS"

# To enable modifying files from the web. Change this too!
# Leave the User field blank in the browser.
CIRCUITPY_WEB_API_PASSWORD="passw0rd"

CIRCUITPY_WEB_API_PORT=80
```

# MatrixPortal S3 Flight Proximity Tracker

## Flight Proximity Code

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/MatrixPortal_S3_Flight_Proximity_Tracker/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
- **airline\_logos** folder
- **code.py**
- **ssl.com-root.pem**

Your Matrix Portal S3 **CIRCUITPY** drive should look like this after copying the **lib** folder, **airline\_logos** folder,&nbsp; **ssl.com-root.pem** file and the **code.py** file.

![Directory](https://adafruit.github.io/Adafruit_Learning_System_Guides/Matrix_Portal_MatrixPortal_S3_Flight_Proximity_Tracker.png The .pem file is not shown but should be included.)

## Importing Necessary Libraries and Modules

This code snippet is essential for setting up our project with multiple features. Using these libraries together allows the fetching and displaying live data from the internet onto the Matrix.

```python
import os
import ssl
import time
import board
import wifi
import terminalio
import socketpool
import adafruit_requests
import displayio
import rgbmatrix
import framebufferio
import adafruit_display_text.label
from displayio import OnDiskBitmap, TileGrid, Group
```

## Using Bounding Box

A bounding box, is a rectangular area defined by two longitudes (east-west lines) and two latitudes (north-south lines). It effectively creates a box on the map that includes a specific area of interest. Here's what each term within the bounding box definition means:

- **`min_latitude`** : The southernmost latitude of the box (bottom edge).
- **`max_latitude`** : The northernmost latitude of the box (top edge).
- **`min_longitude`** : The westernmost longitude of the box (left edge).
- **`max_longitude`** : The easternmost longitude of the box (right edge).

```auto
# Sample Bounding Box
bounding_box = {
    "min_latitude": 40.633013,  # Southernmost latitude
    "max_latitude": 44.953469,  # Northernmost latitude
    "min_longitude": -111.045360,  # Westernmost longitude
    "max_longitude": -104.046570,  # Easternmost longitude
}
```

For this project, the goal is to find flights I can see from my location. I visited [boundingbox.klokantech.com/](https://boundingbox.klokantech.com) to get bounding box coordinates in my area.

![leds_Screenshot_2023-11-17_at_3.27.27_PM.png](https://cdn-learn.adafruit.com/assets/assets/000/126/110/medium640/leds_Screenshot_2023-11-17_at_3.27.27_PM.png?1700254592)

## Setting Up Matrix Display

An `RGBMatrix`&nbsp;object is created with dimensions 128x64 and passed to `FramebufferDisplay.`

```auto
# --- Matrix setup ---
BIT_DEPTH = 2
matrix = rgbmatrix.RGBMatrix(
    width=DISPLAY_WIDTH,
    height=DISPLAY_HEIGHT,
    bit_depth=BIT_DEPTH,
    rgb_pins=[
        board.MTX_B1,
        board.MTX_G1,
        board.MTX_R1,
        board.MTX_B2,
        board.MTX_G2,
        board.MTX_R2,
    ],
    addr_pins=[
        board.MTX_ADDRA,
        board.MTX_ADDRB,
        board.MTX_ADDRC,
        board.MTX_ADDRD,
        board.MTX_ADDRE,
    ],
    clock_pin=board.MTX_CLK,
    latch_pin=board.MTX_LAT,
    output_enable_pin=board.MTX_OE,
    tile=1,
    serpentine=True,
    doublebuffer=True,
)

display = framebufferio.FramebufferDisplay(matrix, auto_refresh=True)
```

## Root Certificate Issue for MatrixPortal S3

The MatrixPortal S3 (running CircuitPython 8.2.7 ) recently faced an issue where it could not retrieve JSON data via HTTPS and gave an `OSError: Failed SSL handshake` error. This problem indicates that the MatrixPortal cannot establish a secure connection with the server, which is necessary for HTTPS communication.

To resolve this issue, it's essential to have a **Root Certificate**. The certificate is provided in the given project folder. This will ensure that you can continue to receive JSON data securely. Be sure to move the Root Certificate file (`root_cert.pem`) to the root directory of your `CIRCUITPY`&nbsp;drive.

## Setting Up Network Connection

This portion focuses on collecting WiFi credentials and establishing a connection using the SSID and password provided through **settings.toml**. If you've not yet set up your credentials, be sure to do so here: [Setting up your Credentials.](https://learn.adafruit.com/rgb-matix-nyt-text-scroller/setting-up-your-credentials)

This section also sets up the network context with the necessary SSL certificates and prepares for making HTTPS requests.

```auto
# --- Wi-Fi setup ---
wifi.radio.connect(
    os.getenv("CIRCUITPY_WIFI_SSID"), os.getenv("CIRCUITPY_WIFI_PASSWORD")
)
print(f"Connected to {os.getenv('CIRCUITPY_WIFI_SSID')}")

# --- Networking setup ---
context = ssl.create_default_context()

with open("/ssl.com-root.pem", "rb") as certfile:
    context.load_verify_locations(cadata=certfile.read())

pool = socketpool.SocketPool(wifi.radio)
requests = adafruit_requests.Session(pool, context)
```

## Scrolling Text

The function `scroll_text_labels` is designed to move a list of text label objects horizontally to the left across the display screen. For each label in the `text_labels` list, it decrements the horizontal position (`label.x`) by 1 pixel, creating a scrolling effect.

```auto
# Function to scroll objects
def scroll_text_labels(text_labels):
    for label in text_labels:
        label.x -= 1  # Move label left.
        if label.x < -300:  # If label has moved off screen.
            label.x = TEXT_RESET_X
```

## Fetching Flight Data

The `fetch_flight_data` function retrieves flight information from the FlightAware AeroAPI within a specified bounding box.

```python
def fetch_flight_data():
    print("Running fetch_flight_data")

    base_url = "https://aeroapi.flightaware.com/aeroapi/flights/search"
    query_prefix = "-latlong+\""
    query_suffix = (
        str(bounding_box['min_latitude']) + "+" +
        str(bounding_box['min_longitude']) + "+" +
        str(bounding_box['max_latitude']) + "+" +
        str(bounding_box['max_longitude']) + "\"")
    query = query_prefix + query_suffix

    params = {
        "query": query,
        "max_pages": "1",}


    headers = {
        "Accept": "application/json; charset=UTF-8",
        "x-apikey": os.getenv("AREO_API_KEY"),  # Replace with your actual API key
    }
    full_url = f"{base_url}?{construct_query_string(params)}"
    response = requests.get(full_url, headers=headers)

    if response.status_code == 200:
        json_response = response.json()  # Parse JSON only once
        return process_flight_data(json_response)  # Process flights and return
    else:
        print(f"Request failed with status code {response.status_code}")
        if response.content:
            print(f"Response content: {response.content}")
        return []

def process_flight_data(json_data):
    # Initialize an empty list to hold processed flight data
    processed_flights = []

    for flight in json_data.get("flights", []):
        # Use 'get' with default values to avoid KeyError
        flight_info = {
            "ident": flight.get("ident", "N/A"),
            "ident_icao": flight.get("ident_icao", "N/A"),
            "fa_flight_id": flight.get("fa_flight_id", "N/A"),
            "actual_off": flight.get("actual_off", "N/A"),
            "actual_on": flight.get("actual_on", "N/A"),
            "origin_code": flight.get("origin", {}).get("code", "UnknownA"),
            "origin_city": flight.get("origin", {}).get("city", "UnknownB"),
            "origin_country": flight.get("origin", {}).get("country", "UnknownC"),
            "destination_code": flight.get("destination", {}).get("code", "UnknownD")
            if flight.get("destination")
            else "UnknownE",
            "destination_city": flight.get("destination", {}).get("city", "UnknownH")
            if flight.get("destination")
            else "Unknown Destination",
            "destination_country": flight.get("destination", {}).get(
                "country", "UnknownZ"
            )
            if flight.get("destination")
            else "UnknownG",
            "altitude": flight.get("last_position", {}).get("altitude", "N/A"),
            "groundspeed": flight.get("last_position", {}).get("groundspeed", "N/A"),
            "heading": flight.get("last_position", {}).get("heading", "N/A"),
            "latitude": flight.get("last_position", {}).get("latitude", "N/A"),
            "longitude": flight.get("last_position", {}).get("longitude", "N/A"),
            "timestamp": flight.get("last_position", {}).get("timestamp", "N/A"),
            "aircraft_type": flight.get("aircraft_type", "N/A"),
        }
        # Only add flight_info if the 'ident_icao' is present and not 'N/A'
        if flight_info["ident_icao"] != "N/A":
            processed_flights.append(flight_info)

    return processed_flights
```

## Creating Text Labels

The function `create_text_labels` is responsible for generating a list of label objects that display flight information on the screen. The vertical positioning of each flight's labels on the screen is determined by the Y positions array and a predefined gap. Each label contains the flight's unique identifier, as well as its origin and destination cities.

```auto
def create_text_labels(flight_data, Ypositions):
    local_text_labels = []
    for i, flight in enumerate(flight_data):
        y_position = Ypositions[i] + GAP_BETWEEN_ICONS

        # Since 'country' is not present, use 'origin_city' and 'destination_city' instead
        origin_city = flight.get("origin_city", "Unknown City")
        destination_city = flight.get("destination_city", "Unknown City")

        # Construct the display text for each flight
        single_line_text = (
            f"{flight['ident']} | From: {origin_city} To: {destination_city}"
        )

        text_label = adafruit_display_text.label.Label(
            FONT, color=TEXT_COLOR, x=TEXT_START_X, y=y_position, text=single_line_text
        )
        local_text_labels.append(text_label)
    return local_text_labels
```

## Creating Airline Icons

This function `create_icon_tilegrid` generates airline icons based on flight identifiers. It retrieves the airline code from the identifier and attempts to load the corresponding icon file for display.

```auto
def create_icon_tilegrid(ident):
    airline_code = ident[:3].upper()  # Use the first three characters of 'ident'
    icon_path = f"/airline_logos/{airline_code}.bmp"

    try:
        file = open(icon_path, "rb")
        icon_bitmap = OnDiskBitmap(file)
    except OSError:
        print(f"Icon for {airline_code} not found. Using placeholder.")
        file = open(PLACEHOLDER_ICON_PATH, "rb")
        icon_bitmap = OnDiskBitmap(file)

    icon_tilegrid = TileGrid(icon_bitmap, pixel_shader=icon_bitmap.pixel_shader, x=0, y=0)
    return icon_tilegrid
```

## Update Display With Flight Data

This `update_display_with_flight_data` function displays new flight data. This function takes two parameters: `flight_data`, a list of dictionaries with data about each flight, and `icon_group` and `display_group`, which are groups of display elements on the LED matrix.

```auto
def update_display_with_flight_data(flight_data, icon_group, display_group):
    # Clear previous display items
    while len(display_group):
        display_group.pop()

    # Clear previous icon items
    while len(icon_group):
        icon_group.pop()

    # Limit flight data to the adjusted number of icons
    flight_data = flight_data[:NUMBER_OF_ICONS]

    # Calculate the y position for each icon
    y_positions = [
        gap_between_icons + (ICON_HEIGHT + gap_between_icons) * i
        for i in range(NUMBER_OF_ICONS)
    ]

#...[]
```

## Show "Display No Flights" text

The `display_no_flights` function handles situations when no flight data is available.&nbsp;

```auto
def display_no_flights():
    # Clear the previous group content
    while len(main_group):
        main_group.pop()

    # Create a label for "Looking for flights..."
    looking_label = adafruit_display_text.label.Label(
        FONT, color=TEXT_COLOR, text="LOOKING FOR FLIGHTS", x=8, y=DISPLAY_HEIGHT // 2
    )
    main_group.append(looking_label)

    # Update the display with the new group
    display.show(main_group)
    display.refresh()
```

```auto
while True:
    # Scroll the text labels
    scroll_text_labels(flight_data_labels)
    # Refresh the display
    display.refresh(minimum_frames_per_second=0)
    current_time = time.monotonic()

    # Check if NETWORK_CALL_INTERVAL seconds have passed
    if (current_time - last_network_call_time) >= NETWORK_CALL_INTERVAL:
        print("Fetching new flight data...")
        new_flight_data = fetch_flight_data()

        if new_flight_data:
            # If flight data is found, update the display with it
            new_text_labels = update_display_with_flight_data(
                new_flight_data, static_icon_group, main_group
            )
        else:
            # If no flight data is found, display the "Looking for flights..." message
            display_no_flights()

        # Reset the last network call time
        last_network_call_time = current_time

        # Sleep for a short period to prevent maxing out your CPU
        time.sleep(1)  # Sleep for 1 second
```

## Main Loop

In the main program loop, flight information text labels are scrolled across the display using the `scroll_text_labels` function. This provides a dynamic view and is achieved by moving each label leftward and wrapping it back to the right once it scrolls off the screen.

```auto
while True:
    # Scroll the text labels
    scroll_text_labels(flight_data_labels)
    # Refresh the display
    display.refresh(minimum_frames_per_second=0)
    current_time = time.monotonic()
```

Depending on what`NETWORK_CALL_INTERVAL`is set to, the program checks if it's time to fetch new flight data. If the interval has passed, it calls `fetch_flight_data()` to retrieve the latest information.

After fetching new data, the display is updated with new flight details using the `update_display_with_flight_data` function. If no new data is available or if an error occurs, a placeholder message, "Looking for flights," is displayed to inform the viewer that the search is ongoing.&nbsp;

```auto
NETWORK_CALL_INTERVAL:
        print("Fetching new flight data...")
        new_flight_data = fetch_flight_data()

            if new_flight_data:
            # If flight data is found, update the display with it
            new_text_labels = update_display_with_flight_data(
                new_flight_data, static_icon_group, main_group
            )
        else:
            # If no flight data is found, display the "Looking for flights..." message
            display_no_flights()

        # Reset the last network call time
        last_network_call_time = current_time
```

Finally, the loop concludes with a brief sleep statement, which pauses the program for one second.

```auto
time.sleep(1)  # Sleep for 1 second
```

# MatrixPortal S3 Flight Proximity Tracker

## Extra Parts

![](https://cdn-learn.adafruit.com/assets/assets/000/126/086/medium800/leds_IMG_3761.png?1700156481)

I purchased two-piece aluminum DIN rails from Amazon to hold the two 64x64 Matrix displays together. These rails measure 12.9 inches in length, 35 mm in width, and 7.5 mm in height. However, since both displays are 10 inches, some parts of the rail are poked out from the sides. Alternatively, you can cut off the protruding parts of the rails.

**I suggest purchasing a smaller DIN rail set, such as 9-10 inches, to comfortably fit the matrix displays and avoid cutting aluminum rails.**

Warning: 

![](https://cdn-learn.adafruit.com/assets/assets/000/126/087/medium800/leds_IMG_3742.png?1700163232)

# MatrixPortal S3 Flight Proximity Tracker

## Assembling The Display

![](https://cdn-learn.adafruit.com/assets/assets/000/126/135/medium800/leds_Matrix-2.png?1700495754)

To ensure proper cable connection, it is recommended first to lay out the cables and check their orientation using the arrow markings present on each matrix. The arrows on both matrices should be pointing upward and to the right.

This first Matrix must have the IDC port on the left side connected to the Matrix Portal S3.

![leds_IMG_3843.png](https://cdn-learn.adafruit.com/assets/assets/000/126/131/medium640/leds_IMG_3843.png?1700495266)

Place the second matrix to the right of the first Matrix. Next, plug an IDC cable into the first Matrix's right port and the second Matrix's left port.

![leds_IMG_3841x.png](https://cdn-learn.adafruit.com/assets/assets/000/126/133/medium640/leds_IMG_3841x.png?1700495442)

## Connecting Matrices

To mount the DIN rails securely, carefully position them to center them over the top and bottom threaded inserts.

Once it is properly aligned, the **magnet holder screws** to the **threaded inserts**. This will ensure that the DIN rail stays firmly in place.

![leds_IMG_3742_11.png](https://cdn-learn.adafruit.com/assets/assets/000/126/119/medium640/leds_IMG_3742_11.png?1700434761)

![leds_IMG_3745qq.png](https://cdn-learn.adafruit.com/assets/assets/000/126/121/medium640/leds_IMG_3745qq.png?1700437341)

## Power Terminals

Next, screw in the spade connectors to the corresponding standoff.

- **red** wire goes to **+5V** &nbsp;
- **black** wire goes to **GND**

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

## Panel Power

To supply power to each panel, you'll need to connect a power plug to the power connector pins. It's important to note that the plug can only be inserted one way, and this way is indicated on the board's silkscreen.

Look for the markings on the board and align the plug accordingly to ensure that it is installed correctly.

![leds_IMG_3749.png](https://cdn-learn.adafruit.com/assets/assets/000/126/127/medium640/leds_IMG_3749.png?1700457042)

# MatrixPortal S3 Flight Proximity Tracker

## LED Matrix Diffuser

## LED Diffusion Acrylic

You can add an [LED diffusion acrylic faceplate](https://www.adafruit.com/product/4594) to the your LED matrix display. (Pictured here with the [ON AIR project](https://learn.adafruit.com/rgb-matrix-automatic-youtube-on-air-sign))

This can help protect the LEDs as well as enhance the look of the sign both indoors and out by reducing glare and specular highlights of the plastic matrix grid.

![led_matrices_onairbuild-3367.jpg](https://cdn-learn.adafruit.com/assets/assets/000/093/987/medium640/led_matrices_onairbuild-3367.jpg?1597088161)

## Measure and Cut the Plastic

You can use the sign to measure and mark cut lines on the paper backing of the acrylic sheet.

Then, use a tablesaw or bandsaw with a fine toothed blade and a guide or sled to make the cuts.

Note: it is possible to score and snap acrylic, but it can be very tricky to get an even snap without proper clamping.

![led_matrices_onairbuild-3343.jpg](https://cdn-learn.adafruit.com/assets/assets/000/093/988/medium640/led_matrices_onairbuild-3343.jpg?1597088278)

![led_matrices_onairbuild-3346.jpg](https://cdn-learn.adafruit.com/assets/assets/000/093/989/medium640/led_matrices_onairbuild-3346.jpg?1597088290)

![led_matrices_onairbuild-3347.jpg](https://cdn-learn.adafruit.com/assets/assets/000/093/991/medium640/led_matrices_onairbuild-3347.jpg?1597089259)

![led_matrices_onairbuild-3349.jpg](https://cdn-learn.adafruit.com/assets/assets/000/093/992/medium640/led_matrices_onairbuild-3349.jpg?1597089270)

![](https://cdn-learn.adafruit.com/assets/assets/000/093/998/medium800/led_matrices_onairbuild-3352.jpg?1597089308)

Peel away the paper backing from both sides and set the acrylic onto your matrix display.

![led_matrices_onairbuild-3355.jpg](https://cdn-learn.adafruit.com/assets/assets/000/093/999/medium640/led_matrices_onairbuild-3355.jpg?1597089331)

## Uglu Dashes

The best method we've found for adhering acrylic to the matrix display is to use [Uglu Dashes clear adhesive rectangles from Pro Tapes](https://www.protapes.com/products/uglu-600-dashes-sheets). They are incredibly strong (although can be removed if necessary), easy to apply, and are invisible once attached.

Use one at each corner and one each at the halfway point of the long edges, then press the acrylic and matrix panel together for about 20 seconds.

![adafruit_io_weathermx-2-3.jpg](https://cdn-learn.adafruit.com/assets/assets/000/094/842/medium640/adafruit_io_weathermx-2-3.jpg?1600463849)

![adafruit_io_weathermx-2.jpg](https://cdn-learn.adafruit.com/assets/assets/000/094/843/medium640/adafruit_io_weathermx-2.jpg?1600463856)

![adafruit_io_weathermx-2-2.jpg](https://cdn-learn.adafruit.com/assets/assets/000/094/844/medium640/adafruit_io_weathermx-2-2.jpg?1600463866)

Here you can see the impact of using the diffusion acrylic. (Pictured here with the ON AIR sign project)

![](https://cdn-learn.adafruit.com/assets/assets/000/094/006/medium800thumb/led_matrices_diffusionOnAir.jpg?1597089757)

## Stand

A very simple and attractive way to display your matrix is with the adjustable [bent-wire stand](https://www.adafruit.com/product/1679).

![led_matrices_onairbuild-3418.jpg](https://cdn-learn.adafruit.com/assets/assets/000/094/032/medium640/led_matrices_onairbuild-3418.jpg?1597094067)

![led_matrices_onairbuild-3419.jpg](https://cdn-learn.adafruit.com/assets/assets/000/094/033/medium640/led_matrices_onairbuild-3419.jpg?1597094085)

![led_matrices_onairbuild-3421.jpg](https://cdn-learn.adafruit.com/assets/assets/000/094/034/medium640/led_matrices_onairbuild-3421.jpg?1597094097)

![led_matrices_onairbuild-3422.jpg](https://cdn-learn.adafruit.com/assets/assets/000/094/035/medium640/led_matrices_onairbuild-3422.jpg?1597094107)

![](https://cdn-learn.adafruit.com/assets/assets/000/095/378/medium800/adafruit_io_led_matrices_onairbuild-3383.jpg?1602006263)

Alternately, you can use a frame, [3D printed brackets](https://learn.adafruit.com/led-protest-sign/build-the-sign#step-3065326), tape, glue, or even large binder clips to secure the acrylic to the sign and then mount it on on a wall, shelf, or display cabinet.

[These mini-magnet feet](https://www.adafruit.com/product/4631) can be used to stick the sign to a ferrous surface.


## 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)
### 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)
### 64x64 RGB LED Matrix - 2.5mm Pitch

[64x64 RGB LED Matrix - 2.5mm Pitch](https://www.adafruit.com/product/3649)
Winter time can be rough in the city. The sky is gray. The weather&nbsp;is unpredictable. So slough off those seasonal blues with&nbsp;some&nbsp;Times Square razzle dazzle from this sweet, ultra-high-density **64x64 RGB LED Matrix**. These panels are typically used to make video...

In Stock
[Buy Now](https://www.adafruit.com/product/3649)
[Related Guides to the Product](https://learn.adafruit.com/products/3649/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)
### Mini Magnet Feet for RGB LED Matrices (Pack of 4)

[Mini Magnet Feet for RGB LED Matrices (Pack of 4)](https://www.adafruit.com/product/4631)
Got a glorious&nbsp;RGB Matrix project you want to mount and display in your workspace or home? If you have one of the matrix panels listed below, you'll need a pack of these **Mini-Magnet Feet.** &nbsp;We got these specifically&nbsp;for our RGB LED Matrices, which no longer...

In Stock
[Buy Now](https://www.adafruit.com/product/4631)
[Related Guides to the Product](https://learn.adafruit.com/products/4631/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)

## Related Guides

- [Adafruit MatrixPortal S3](https://learn.adafruit.com/adafruit-matrixportal-s3.md)
- [Blurry Analog Clock](https://learn.adafruit.com/blurry-analog-clock.md)
- [Matrix Portal Stained Glass with WLED](https://learn.adafruit.com/matrix-portal-stained-glass-with-wled.md)
- [Scroll an SMS Text Message on your RGB Matrix](https://learn.adafruit.com/scroll-an-sms-text-message-on-your-rgb-matrix.md)
- [itsaSNAP Daily Weather Forecast Board](https://learn.adafruit.com/itsasnap-daily-weather-forecast-board.md)
- [RGB Matrix New York Times Title Scroller](https://learn.adafruit.com/rgb-matix-nyt-text-scroller.md)
- [LED Matrix FIFA World Cup Scoreboard](https://learn.adafruit.com/led-matrix-fifa-world-cup-scoreboard.md)
- [Animated GIF Player for Matrix Portal](https://learn.adafruit.com/animated-gif-player-for-matrix-portal.md)
- [itsaSNAP Apple Health Status Board](https://learn.adafruit.com/itssnap-apple-fitness-status-board.md)
- [LED Matrix Sports Scoreboard](https://learn.adafruit.com/led-matrix-sports-scoreboard.md)
- [MatrixPortal CircuitPython Animated Message Board](https://learn.adafruit.com/matrixportal-circuitpython-animated-message-board.md)
- [Jack-o-Theremin](https://learn.adafruit.com/jack-o-theremin.md)
- [Adafruit Microphone Amplifier Breakout](https://learn.adafruit.com/adafruit-microphone-amplifier-breakout.md)
- [Adafruit Feather RP2350 with HSTX](https://learn.adafruit.com/adafruit-feather-rp2350.md)
- [MagTag Daily Christmas Countdown](https://learn.adafruit.com/magtag-daily-christmas-countdown.md)
