I work near a municipal airport and find myself staring out the window sometimes wondering what all of the planes passing overhead are and where they are coming from. PlanePortal is a weekend project that turns an Adafruit PyPortal into a live flight tracker for your desk. It watches a configurable radius around any point you choose, pulls live aircraft positions from the OpenSky Network, enriches them with route and airline data from ADSBDB, and renders everything on a compact aviation-style dashboard with mini radar, featured aircraft card, and a scrolling list of recent traffic.
Prerequisite: Gather your MaterialsAt minimum you will require an Adafruit PyPortal. Optionally, acquire some standoffs and screws to attach it to the 3D printed stand Adafruit has released on Thingiverse. The project will work just fine on a standalone Portal, the stand simply makes it easier to display on a desk.
Step 1: Install CircuitPythonIf your PyPortal isn't already running CircuitPython, first follow these directions from Adafruit about how to load it. This takes only a few minutes. This is a delightful language that lets you write code your embedded platforms using Python syntax.
Your PyPortal should be connected to your computer via USB. Leave it connected. You should have a CIRCUITPY drive connected to your machine if CircuitPython has been installed correctly. This behaves like a flashdrive, or external hard drive. We will be copying files to it in later steps.
Download the Adafruit CircuitPython bundle that matches your CircuitPython version by following this guide from Adafruit. From the bundle, copy these into the CIRCUITPY/lib folder:
adafruit_connection_manager.mpyadafruit_requests.mpyadafruit_display_text/(the whole folder)adafruit_esp32spi/(the whole folder)
PlanePortal uses the built-in board, displayio, and terminalio modules, so no extra fonts or image assets are needed.
Head to opensky-network.org and create a free account. Once you're logged in, navigate to your account settings at https://opensky-network.org/my-opensky/account to find API client credentials (a client ID and client secret). There should be a "Download Credentials" button. PlanePortal uses OAuth2 client credentials to authenticate. A file will download to your computer. Keep this information safe and secret. Only copy it into your settings file in an upcoming step.
Step 4: Find Your Watch Point CoordinatesDecide what spot you want to monitor. It could be your house, your office, or a nearby airport. Look up the latitude and longitude — Google Maps makes this easy (right-click any spot and the coordinates appear). Jot these down.
Step 5: Configure settings.tomlDownload the code repository to your system: https://github.com/kevinl95/PlanePortal
You can either click the green "Code" button and download the repo as a zip, or install Git and clone it to the location of your choice.
Copy settings.toml.example from the repo to CIRCUITPY/settings.toml and fill in your values. The OpenSky ID and secret match the credentials you downloaded in Step 3.
CIRCUITPY_WIFI_SSID = "your_wifi_name"
CIRCUITPY_WIFI_PASSWORD = "your_wifi_password"
OPENSKY_CLIENT_ID = "your_opensky_client_id"
OPENSKY_CLIENT_SECRET = "your_opensky_client_secret"
PLANEPORTAL_HOME_LATITUDE = "40.0340"
PLANEPORTAL_HOME_LONGITUDE = "-105.2260"
PLANEPORTAL_RADIUS_MILES = "3"A few things to note: latitudes and longitudes must be quoted strings because CircuitPython's settings.toml doesn't support float numbers. The radius defaults to 3 miles, which is a good starting point. You can widen it if your area has lighter traffic or tighten it if you're swamped.
There are also optional tuning parameters you can adjust: PLANEPORTAL_REFRESH_SECONDS (default 120) controls how often it polls for new data, PLANEPORTAL_RECENT_WINDOW_MINUTES (default 10) controls how long a plane stays in the "recently seen" list after leaving your radius, and PLANEPORTAL_ENRICHMENT_LIMIT (default 4) caps how many aircraft get the full ADSBDB metadata lookup per cycle (to stay within rate limits).
Note: It is normal if your PyPortal reboots after you save this file! It will automatically restart any time you add or change a file on the CIRCUITPY drive to load the latest changes.
Copy code.py from the repo to the root of the CIRCUITPY drive, and copy the entire app/ folder alongside it. Your drive should look like:
CIRCUITPY/
├── code.py
├── app/
│ ├── main.py
│ ├── config.py
│ ├── network.py
│ ├── opensky_client.py
│ ├── adsbdb_client.py
│ ├── tracker.py
│ └── ui.py
├── lib/
│ ├── adafruit_connection_manager.mpy
│ ├── adafruit_requests.mpy
│ ├── adafruit_display_text/
│ └── adafruit_esp32spi/
└── settings.tomlStep 7: Power Up and WatchReset the PyPortal (or just unplug and replug it). It will connect to your WiFi, authenticate with OpenSky, and start scanning for aircraft. Within a couple of minutes you should see the dashboard populate: the mini radar on the left plotting planes by bearing and distance, a featured aircraft card with callsign, route, altitude, speed, and climb/descent trend, and the recent traffic list on the right.
If something goes wrong, connect to the serial console (using screen, PuTTY, or Mu) and look for lines starting with Plane Portal error: — they'll tell you exactly what happened.
The stand can be printed using this model from Adafruit. I used gray PLA. I recommend you print the stand on its side to avoid needing supports.
Simply align your PyPortal with the printed stand and use your preferred stand offs and screws to secure it.
How It Works Under the HoodPlanePortal polls the OpenSky Network API for live ADS-B data within a configurable area around your watch point. Aircraft that pass the filter get tracked in a rolling memory so they don't vanish the instant they leave your radius.
For the most relevant planes, it reaches out to ADSBDB, which is a community aircraft database, to enrich the raw transponder data with registration, aircraft type, airline, and route information. This is best-effort; not every aircraft has complete metadata, but it's often good for commercial traffic.
If you see "metadata delayed" in the footer, that just means some aircraft haven't been enriched yet and they'll get picked up on the next cycle. Not every aircraft has route information in ADSBDB, so "NO ROUTE" for some entries is normal, especially for general aviation traffic.
I hope you have as much fun watching planes fly by as I had building PlanePortal!












Comments